feat(@desktop/wallet): Networks - New List UX with link mainnet/testnet + testnet mode enable/disable

fixes #11252
This commit is contained in:
Khushboo Mehta 2023-06-27 21:56:44 +02:00 committed by Khushboo-dev-cpp
parent 5b118f43f8
commit a468635ddc
21 changed files with 221 additions and 116 deletions

View File

@ -97,7 +97,7 @@ let DEFAULT_TORRENT_CONFIG_TORRENTDIR* = joinPath(main_constants.defaultDataDir(
var NETWORKS* = %* [ var NETWORKS* = %* [
{ {
"chainId": 1, "chainId": 1,
"chainName": "Ethereum Mainnet", "chainName": "Mainnet",
"rpcUrl": "https://eth-archival.gateway.pokt.network/v1/lb/" & POKT_TOKEN_RESOLVED, "rpcUrl": "https://eth-archival.gateway.pokt.network/v1/lb/" & POKT_TOKEN_RESOLVED,
"fallbackUrl": "https://mainnet.infura.io/v3/" & INFURA_TOKEN_RESOLVED, "fallbackUrl": "https://mainnet.infura.io/v3/" & INFURA_TOKEN_RESOLVED,
"blockExplorerUrl": "https://etherscan.io/", "blockExplorerUrl": "https://etherscan.io/",
@ -110,22 +110,24 @@ var NETWORKS* = %* [
"isTest": false, "isTest": false,
"layer": 1, "layer": 1,
"enabled": true, "enabled": true,
"relatedChainId": 5,
}, },
{ {
"chainId": 5, "chainId": 5,
"chainName": "Goerli", "chainName": "Mainnet",
"rpcUrl": "https://goerli-archival.gateway.pokt.network/v1/lb/" & POKT_TOKEN_RESOLVED, "rpcUrl": "https://goerli-archival.gateway.pokt.network/v1/lb/" & POKT_TOKEN_RESOLVED,
"fallbackUrl": "https://goerli.infura.io/v3/" & INFURA_TOKEN_RESOLVED, "fallbackUrl": "https://goerli.infura.io/v3/" & INFURA_TOKEN_RESOLVED,
"blockExplorerUrl": "https://goerli.etherscan.io/", "blockExplorerUrl": "https://goerli.etherscan.io/",
"iconUrl": "network/Network=Testnet", "iconUrl": "network/Network=Ethereum",
"chainColor": "#939BA1", "chainColor": "#627EEA",
"shortName": "goEth", "shortName": "eth",
"nativeCurrencyName": "Ether", "nativeCurrencyName": "Ether",
"nativeCurrencySymbol": "ETH", "nativeCurrencySymbol": "ETH",
"nativeCurrencyDecimals": 18, "nativeCurrencyDecimals": 18,
"isTest": true, "isTest": true,
"layer": 1, "layer": 1,
"enabled": true, "enabled": true,
"relatedChainId": 1,
}, },
{ {
"chainId": 10, "chainId": 10,
@ -142,22 +144,24 @@ var NETWORKS* = %* [
"isTest": false, "isTest": false,
"layer": 2, "layer": 2,
"enabled": true, "enabled": true,
"relatedChainId": 420,
}, },
{ {
"chainId": 420, "chainId": 420,
"chainName": "Optimism Goerli Testnet", "chainName": "Optimism",
"rpcUrl": "https://optimism-goerli.infura.io/v3/" & INFURA_TOKEN_RESOLVED, "rpcUrl": "https://optimism-goerli.infura.io/v3/" & INFURA_TOKEN_RESOLVED,
"fallbackUrl": "", "fallbackUrl": "",
"blockExplorerUrl": "https://goerli-optimism.etherscan.io/", "blockExplorerUrl": "https://goerli-optimism.etherscan.io/",
"iconUrl": "network/Network=Testnet", "iconUrl": "network/Network=Optimism",
"chainColor": "#939BA1", "chainColor": "#E90101",
"shortName": "goOpt", "shortName": "opt",
"nativeCurrencyName": "Ether", "nativeCurrencyName": "Ether",
"nativeCurrencySymbol": "ETH", "nativeCurrencySymbol": "ETH",
"nativeCurrencyDecimals": 18, "nativeCurrencyDecimals": 18,
"isTest": true, "isTest": true,
"layer": 2, "layer": 2,
"enabled": false, "enabled": false,
"relatedChainId": 10,
}, },
{ {
"chainId": 42161, "chainId": 42161,
@ -174,22 +178,24 @@ var NETWORKS* = %* [
"isTest": false, "isTest": false,
"layer": 2, "layer": 2,
"enabled": true, "enabled": true,
"relatedChainId": 421613,
}, },
{ {
"chainId": 421613, "chainId": 421613,
"chainName": "Arbitrum Goerli", "chainName": "Arbitrum",
"rpcUrl": "https://arbitrum-goerli.infura.io/v3/" & INFURA_TOKEN_RESOLVED, "rpcUrl": "https://arbitrum-goerli.infura.io/v3/" & INFURA_TOKEN_RESOLVED,
"fallbackUrl": "", "fallbackUrl": "",
"blockExplorerUrl": "https://goerli.arbiscan.io/", "blockExplorerUrl": "https://goerli.arbiscan.io/",
"iconUrl": "network/Network=Testnet", "iconUrl": "network/Network=Arbitrum",
"chainColor": "#939BA1", "chainColor": "#51D0F0",
"shortName": "goArb", "shortName": "arb",
"nativeCurrencyName": "Ether", "nativeCurrencyName": "Ether",
"nativeCurrencySymbol": "ETH", "nativeCurrencySymbol": "ETH",
"nativeCurrencyDecimals": 18, "nativeCurrencyDecimals": 18,
"isTest": true, "isTest": true,
"layer": 2, "layer": 2,
"enabled": false, "enabled": false,
"relatedChainId": 42161,
} }
] ]
@ -197,7 +203,7 @@ if GANACHE_NETWORK_RPC_URL != "":
NETWORKS = %* [ NETWORKS = %* [
{ {
"chainId": 1, "chainId": 1,
"chainName": "Ethereum Mainnet", "chainName": "Mainnet",
"rpcUrl": GANACHE_NETWORK_RPC_URL, "rpcUrl": GANACHE_NETWORK_RPC_URL,
"fallbackUrl": GANACHE_NETWORK_RPC_URL, "fallbackUrl": GANACHE_NETWORK_RPC_URL,
"blockExplorerUrl": "https://etherscan.io/", "blockExplorerUrl": "https://etherscan.io/",
@ -215,17 +221,18 @@ if GANACHE_NETWORK_RPC_URL != "":
"symbol": "SNT", "symbol": "SNT",
"address": "0x8571Ddc46b10d31EF963aF49b6C7799Ea7eff818" "address": "0x8571Ddc46b10d31EF963aF49b6C7799Ea7eff818"
} }
] ],
"relatedChainId": 5,
}, },
{ {
"chainId": 5, "chainId": 5,
"chainName": "Goerli", "chainName": "Mainnet",
"rpcUrl": GANACHE_NETWORK_RPC_URL, "rpcUrl": GANACHE_NETWORK_RPC_URL,
"fallbackUrl": GANACHE_NETWORK_RPC_URL, "fallbackUrl": GANACHE_NETWORK_RPC_URL,
"blockExplorerUrl": "https://goerli.etherscan.io/", "blockExplorerUrl": "https://goerli.etherscan.io/",
"iconUrl": "network/Network=Testnet", "iconUrl": "network/Network=Ethereum",
"chainColor": "#939BA1", "chainColor": "#627EEA",
"shortName": "goEth", "shortName": "eth",
"nativeCurrencyName": "Ether", "nativeCurrencyName": "Ether",
"nativeCurrencySymbol": "ETH", "nativeCurrencySymbol": "ETH",
"nativeCurrencyDecimals": 18, "nativeCurrencyDecimals": 18,
@ -237,7 +244,8 @@ if GANACHE_NETWORK_RPC_URL != "":
"symbol": "STT", "symbol": "STT",
"address": "0x8571Ddc46b10d31EF963aF49b6C7799Ea7eff818" "address": "0x8571Ddc46b10d31EF963aF49b6C7799Ea7eff818"
} }
] ],
"relatedChainId": 1,
}, },
{ {
"chainId": 10, "chainId": 10,
@ -254,22 +262,24 @@ if GANACHE_NETWORK_RPC_URL != "":
"isTest": false, "isTest": false,
"layer": 2, "layer": 2,
"enabled": true, "enabled": true,
"relatedChainId": 420,
}, },
{ {
"chainId": 420, "chainId": 420,
"chainName": "Optimism Goerli Testnet", "chainName": "Optimism",
"rpcUrl": GANACHE_NETWORK_RPC_URL, "rpcUrl": GANACHE_NETWORK_RPC_URL,
"fallbackUrl": GANACHE_NETWORK_RPC_URL, "fallbackUrl": GANACHE_NETWORK_RPC_URL,
"blockExplorerUrl": "https://goerli-optimism.etherscan.io/", "blockExplorerUrl": "https://goerli-optimism.etherscan.io/",
"iconUrl": "network/Network=Testnet", "iconUrl": "network/Network=Optimism",
"chainColor": "#939BA1", "chainColor": "#E90101",
"shortName": "goOpt", "shortName": "opt",
"nativeCurrencyName": "Ether", "nativeCurrencyName": "Ether",
"nativeCurrencySymbol": "ETH", "nativeCurrencySymbol": "ETH",
"nativeCurrencyDecimals": 18, "nativeCurrencyDecimals": 18,
"isTest": true, "isTest": true,
"layer": 2, "layer": 2,
"enabled": false, "enabled": false,
"relatedChainId": 10,
}, },
{ {
"chainId": 42161, "chainId": 42161,
@ -286,22 +296,24 @@ if GANACHE_NETWORK_RPC_URL != "":
"isTest": false, "isTest": false,
"layer": 2, "layer": 2,
"enabled": true, "enabled": true,
"relatedChainId": 421613,
}, },
{ {
"chainId": 421613, "chainId": 421613,
"chainName": "Arbitrum Goerli", "chainName": "Arbitrum",
"rpcUrl": GANACHE_NETWORK_RPC_URL, "rpcUrl": GANACHE_NETWORK_RPC_URL,
"fallbackUrl": GANACHE_NETWORK_RPC_URL, "fallbackUrl": GANACHE_NETWORK_RPC_URL,
"blockExplorerUrl": "https://goerli.arbiscan.io/", "blockExplorerUrl": "https://goerli.arbiscan.io/",
"iconUrl": "network/Network=Testnet", "iconUrl": "network/Network=Arbitrum",
"chainColor": "#939BA1", "chainColor": "#51D0F0",
"shortName": "goArb", "shortName": "arb",
"nativeCurrencyName": "Ether", "nativeCurrencyName": "Ether",
"nativeCurrencySymbol": "ETH", "nativeCurrencySymbol": "ETH",
"nativeCurrencyDecimals": 18, "nativeCurrencyDecimals": 18,
"isTest": true, "isTest": true,
"layer": 2, "layer": 2,
"enabled": false, "enabled": false,
"relatedChainId": 42161,
} }
] ]

View File

@ -18,6 +18,11 @@ type NetworkDto* = ref object
enabled* {.serializedFieldName("enabled").}: bool enabled* {.serializedFieldName("enabled").}: bool
chainColor* {.serializedFieldName("chainColor").}: string chainColor* {.serializedFieldName("chainColor").}: string
shortName* {.serializedFieldName("shortName").}: string shortName* {.serializedFieldName("shortName").}: string
relatedChainId* {.serializedFieldName("relatedChainId").}: int
type CombinedNetworkDto* = ref object
prod* {.serializedFieldName("Prod").}: NetworkDto
test* {.serializedFieldName("Test").}: NetworkDto
proc `$`*(self: NetworkDto): string = proc `$`*(self: NetworkDto): string =
return fmt"""Network( return fmt"""Network(
@ -34,7 +39,8 @@ proc `$`*(self: NetworkDto): string =
nativeCurrencySymbol:{self.nativeCurrencySymbol}, nativeCurrencySymbol:{self.nativeCurrencySymbol},
isTest:{self.isTest}, enabled:{self.enabled}, isTest:{self.isTest}, enabled:{self.enabled},
chainColor:{self.chainColor}, chainColor:{self.chainColor},
shortName:{self.shortName} shortName:{self.shortName},
relatedChainId:{self.relatedChainId}
)""" )"""
proc hash*(self: NetworkDto): Hash = proc hash*(self: NetworkDto): Hash =

View File

@ -14,7 +14,7 @@ logScope:
type type
Service* = ref object of RootObj Service* = ref object of RootObj
events: EventEmitter events: EventEmitter
networks: seq[NetworkDto] networks: seq[CombinedNetworkDto]
networksInited: bool networksInited: bool
dirty: Atomic[bool] dirty: Atomic[bool]
settingsService: settings_service.Service settingsService: settings_service.Service
@ -31,16 +31,16 @@ proc newService*(events: EventEmitter, settingsService: settings_service.Service
proc init*(self: Service) = proc init*(self: Service) =
discard discard
proc fetchNetworks*(self: Service, useCached: bool = true): seq[NetworkDto] = proc fetchNetworks*(self: Service, useCached: bool = true): seq[CombinedNetworkDto] =
let cacheIsDirty = not self.networksInited or self.dirty.load let cacheIsDirty = not self.networksInited or self.dirty.load
if useCached and not cacheIsDirty: if useCached and not cacheIsDirty:
result = self.networks result = self.networks
else: else:
let response = backend.getEthereumChains(false) let response = backend.getEthereumChains()
if not response.error.isNil: if not response.error.isNil:
raise newException(Exception, "Error getting networks: " & response.error.message) raise newException(Exception, "Error getting networks: " & response.error.message)
result = if response.result.isNil or response.result.kind == JNull: @[] result = if response.result.isNil or response.result.kind == JNull: @[]
else: Json.decode($response.result, seq[NetworkDto], allowUnknownFields = true) else: Json.decode($response.result, seq[CombinedNetworkDto], allowUnknownFields = true)
self.dirty.store(false) self.dirty.store(false)
self.networks = result self.networks = result
self.networksInited = true self.networksInited = true
@ -49,11 +49,10 @@ proc getNetworks*(self: Service): seq[NetworkDto] =
let testNetworksEnabled = self.settingsService.areTestNetworksEnabled() let testNetworksEnabled = self.settingsService.areTestNetworksEnabled()
for network in self.fetchNetworks(): for network in self.fetchNetworks():
if testNetworksEnabled and network.isTest: if testNetworksEnabled:
result.add(network) result.add(network.test)
else:
if not testNetworksEnabled and not network.isTest: result.add(network.prod)
result.add(network)
proc upsertNetwork*(self: Service, network: NetworkDto) = proc upsertNetwork*(self: Service, network: NetworkDto) =
discard backend.addEthereumChain(backend.Network( discard backend.addEthereumChain(backend.Network(
@ -71,6 +70,7 @@ proc upsertNetwork*(self: Service, network: NetworkDto) =
enabled: network.enabled, enabled: network.enabled,
chainColor: network.chainColor, chainColor: network.chainColor,
shortName: network.shortName, shortName: network.shortName,
relatedChainID: network.relatedChainID,
)) ))
self.dirty.store(true) self.dirty.store(true)
@ -79,14 +79,20 @@ proc deleteNetwork*(self: Service, network: NetworkDto) =
self.dirty.store(true) self.dirty.store(true)
proc getNetwork*(self: Service, chainId: int): NetworkDto = proc getNetwork*(self: Service, chainId: int): NetworkDto =
let testNetworksEnabled = self.settingsService.areTestNetworksEnabled()
for network in self.fetchNetworks(): for network in self.fetchNetworks():
if chainId == network.chainId: let net = if testNetworksEnabled: network.test
return network else: network.prod
if chainId == net.chainId:
return net
proc getNetwork*(self: Service, networkType: NetworkType): NetworkDto = proc getNetwork*(self: Service, networkType: NetworkType): NetworkDto =
let testNetworksEnabled = self.settingsService.areTestNetworksEnabled()
for network in self.fetchNetworks(): for network in self.fetchNetworks():
if networkType.toChainId() == network.chainId: let net = if testNetworksEnabled: network.test
return network else: network.prod
if networkType.toChainId() == net.chainId:
return net
# Will be removed, this is used in case of legacy chain Id # Will be removed, this is used in case of legacy chain Id
return NetworkDto(chainId: networkType.toChainId()) return NetworkDto(chainId: networkType.toChainId())

View File

@ -120,19 +120,15 @@ QtObject:
var allDown: bool = true var allDown: bool = true
var chaindIdsDown: seq[int] = @[] var chaindIdsDown: seq[int] = @[]
# checking all down we check all networks and for chainIds to be displayed as down let allChainIds = self.networkService.getNetworks().map(a => a.chainId)
# we only check for networks currently active (for test net only testnet networks etc...)
let currentChainIds = self.networkService.getNetworks().map(a => a.chainId)
let allChainIds = self.networkService.fetchNetworks().map(a => a.chainId)
if chainStatusTable.kind != JNull: if chainStatusTable.kind != JNull:
for id in allChainIds: for id in allChainIds:
if chainStatusTable[$id].kind != JNull: if chainStatusTable[$id].kind != JNull:
let isDown = self.getIsDown(chainStatusTable[$id].getStr) let isDown = self.getIsDown(chainStatusTable[$id].getStr)
if not isDown: if isDown:
chaindIdsDown.add(id)
else:
allDown = false allDown = false
if currentChainIds.contains(id):
if isDown:
chaindIdsDown.add(id)
return (allDown, chaindIdsDown) return (allDown, chaindIdsDown)
proc getFormattedStringForChainIds(self: Service, chainIds: seq[int]): string = proc getFormattedStringForChainIds(self: Service, chainIds: seq[int]): string =

View File

@ -49,6 +49,7 @@ type
enabled* {.serializedFieldName("enabled").}: bool enabled* {.serializedFieldName("enabled").}: bool
chainColor* {.serializedFieldName("chainColor").}: string chainColor* {.serializedFieldName("chainColor").}: string
shortName* {.serializedFieldName("shortName").}: string shortName* {.serializedFieldName("shortName").}: string
relatedChainID* {.serializedFieldName("relatedChainID").}: int
ActivityCenterNotificationsRequest* = ref object of RootObj ActivityCenterNotificationsRequest* = ref object of RootObj
cursor* {.serializedFieldName("cursor").}: string cursor* {.serializedFieldName("cursor").}: string
@ -64,7 +65,7 @@ rpc(clientVersion, "web3"):
discard discard
rpc(getEthereumChains, "wallet"): rpc(getEthereumChains, "wallet"):
onlyEnabled: bool discard
rpc(addEthereumChain, "wallet"): rpc(addEthereumChain, "wallet"):
network: Network network: Network

View File

@ -5,7 +5,7 @@ import QtQuick.Layouts 1.14
import Storybook 1.0 import Storybook 1.0
import Models 1.0 import Models 1.0
import AppLayouts.Communities.popups 1.0 import shared.popups 1.0
SplitView { SplitView {
Logs { id: logs } Logs { id: logs }

View File

@ -238,7 +238,7 @@ SplitView {
Label { text: "Type:" } Label { text: "Type:" }
ComboBox { ComboBox {
id: ctrlType id: ctrlType
model: ["Normal", "Danger", "Primary"] // enum StatusBaseButton.Type.xxx model: ["Normal", "Danger", "Primary", "Warning"] // enum StatusBaseButton.Type.xxx
} }
} }
RowLayout { RowLayout {

View File

@ -168,10 +168,10 @@ mainWallet_Saved_Addreses_Popup_Address_Add_Button = {"container": statusDesktop
mainWallet_Saved_Addreses_Popup_Add_Network_Selector = {"container": statusDesktop_mainWindow, "objectName": "addSavedAddressNetworkSelector", "type": "StatusNetworkSelector", "visible": True} mainWallet_Saved_Addreses_Popup_Add_Network_Selector = {"container": statusDesktop_mainWindow, "objectName": "addSavedAddressNetworkSelector", "type": "StatusNetworkSelector", "visible": True}
mainWallet_Saved_Addreses_Popup_Add_Network_Button = {"container": statusDesktop_mainWindow_overlay, "objectName": "addNetworkTagItemButton", "type": "StatusRoundButton", "visible": True} mainWallet_Saved_Addreses_Popup_Add_Network_Button = {"container": statusDesktop_mainWindow_overlay, "objectName": "addNetworkTagItemButton", "type": "StatusRoundButton", "visible": True}
mainWallet_Saved_Addreses_Popup_Add_Network_Selector_Tag = {"container": statusDesktop_mainWindow_overlay, "objectName": "networkSelectorTag", "type": "StatusNetworkListItemTag"} mainWallet_Saved_Addreses_Popup_Add_Network_Selector_Tag = {"container": statusDesktop_mainWindow_overlay, "objectName": "networkSelectorTag", "type": "StatusNetworkListItemTag"}
mainWallet_Saved_Addresses_Popup_Add_Network_Selector_Mainnet_checkbox = {"container": statusDesktop_mainWindow_overlay, "objectName": "networkSelectionCheckbox_Ethereum Mainnet", "type": "StatusCheckBox", "visible": True} mainWallet_Saved_Addresses_Popup_Add_Network_Selector_Mainnet_checkbox = {"container": statusDesktop_mainWindow_overlay, "objectName": "networkSelectionCheckbox_Mainnet", "type": "StatusCheckBox", "visible": True}
mainWallet_Saved_Addresses_Popup_Add_Network_Selector_Optimism_checkbox = {"container": statusDesktop_mainWindow_overlay, "objectName": "networkSelectionCheckbox_Optimism", "type": "StatusCheckBox", "visible": True} mainWallet_Saved_Addresses_Popup_Add_Network_Selector_Optimism_checkbox = {"container": statusDesktop_mainWindow_overlay, "objectName": "networkSelectionCheckbox_Optimism", "type": "StatusCheckBox", "visible": True}
mainWallet_Saved_Addresses_Popup_Add_Network_Selector_Arbitrum_checkbox = {"container": statusDesktop_mainWindow_overlay, "objectName": "networkSelectionCheckbox_Arbitrum", "type": "StatusCheckBox", "visible": True} mainWallet_Saved_Addresses_Popup_Add_Network_Selector_Arbitrum_checkbox = {"container": statusDesktop_mainWindow_overlay, "objectName": "networkSelectionCheckbox_Arbitrum", "type": "StatusCheckBox", "visible": True}
mainWallet_Saved_Addresses_Popup_Network_Selector_Mainnet_network_tag = {"container": statusDesktop_mainWindow_overlay, "objectName": "networkTagRectangle_Ethereum Mainnet", "type": "Rectangle", "visible": True} mainWallet_Saved_Addresses_Popup_Network_Selector_Mainnet_network_tag = {"container": statusDesktop_mainWindow_overlay, "objectName": "networkTagRectangle_Mainnet", "type": "Rectangle", "visible": True}
mainWallet_Saved_Addresses_Popup_Network_Selector_Optimism_network_tag = {"container": statusDesktop_mainWindow_overlay, "objectName": "networkTagRectangle_Optimism", "type": "Rectangle", "visible": True} mainWallet_Saved_Addresses_Popup_Network_Selector_Optimism_network_tag = {"container": statusDesktop_mainWindow_overlay, "objectName": "networkTagRectangle_Optimism", "type": "Rectangle", "visible": True}
mainWallet_Saved_Addresses_Popup_Network_Selector_Arbitrum_network_tag = {"container": statusDesktop_mainWindow_overlay, "objectName": "networkTagRectangle_Arbitrum", "type": "Rectangle", "visible": True} mainWallet_Saved_Addresses_Popup_Network_Selector_Arbitrum_network_tag = {"container": statusDesktop_mainWindow_overlay, "objectName": "networkTagRectangle_Arbitrum", "type": "Rectangle", "visible": True}
# Collectibles view # Collectibles view

View File

@ -19,7 +19,8 @@ Button {
enum Type { enum Type {
Normal, Normal,
Danger, Danger,
Primary Primary,
Warning
} }
enum TextPosition { enum TextPosition {

View File

@ -6,15 +6,19 @@ StatusBaseButton {
id: statusButton id: statusButton
normalColor: type === StatusBaseButton.Type.Primary ? Theme.palette.primaryColor1 : normalColor: type === StatusBaseButton.Type.Primary ? Theme.palette.primaryColor1 :
type === StatusBaseButton.Type.Normal ? Theme.palette.primaryColor3 type === StatusBaseButton.Type.Normal ? Theme.palette.primaryColor3 :
: Theme.palette.dangerColor3 type === StatusBaseButton.Type.Warning ? Theme.palette.warningColor3
: Theme.palette.dangerColor3
hoverColor: type === StatusBaseButton.Type.Primary ? Theme.palette.hoverColor(normalColor) : hoverColor: type === StatusBaseButton.Type.Primary ? Theme.palette.hoverColor(normalColor) :
type === StatusBaseButton.Type.Normal ? Theme.palette.primaryColor2 type === StatusBaseButton.Type.Normal ? Theme.palette.primaryColor2 :
: Theme.palette.dangerColor2 type === StatusBaseButton.Type.Warning ? Theme.palette.warningColor2
: Theme.palette.dangerColor2
disabledColor: Theme.palette.baseColor2 disabledColor: Theme.palette.baseColor2
textColor: type === StatusBaseButton.Type.Primary ? Theme.palette.white : textColor: type === StatusBaseButton.Type.Primary ? Theme.palette.white :
type === StatusBaseButton.Type.Normal ? Theme.palette.primaryColor1 type === StatusBaseButton.Type.Normal ? Theme.palette.primaryColor1 :
: Theme.palette.dangerColor1 type === StatusBaseButton.Type.Warning ? Theme.palette.warningColor1
: Theme.palette.dangerColor1
disabledTextColor: Theme.palette.baseColor1 disabledTextColor: Theme.palette.baseColor1
} }

View File

@ -15,6 +15,7 @@ import AppLayouts.Communities.views 1.0
import shared.controls 1.0 import shared.controls 1.0
import utils 1.0 import utils 1.0
import shared.popups 1.0
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
StackView { StackView {

View File

@ -6,6 +6,7 @@ import StatusQ.Core.Theme 0.1
StatusListItem { StatusListItem {
property var network property var network
property bool areTestNetworksEnabled
title: network.chainName title: network.chainName
asset.name: Style.svg(network.iconUrl) asset.name: Style.svg(network.iconUrl)
asset.isImage: true asset.isImage: true
@ -13,6 +14,12 @@ StatusListItem {
leftPadding: Style.current.padding leftPadding: Style.current.padding
rightPadding: Style.current.padding rightPadding: Style.current.padding
components: [ components: [
StatusBaseText {
text: qsTr("Goerli testnet active")
font.pixelSize: 15
color: Theme.palette.baseColor1
visible: areTestNetworksEnabled
},
StatusIcon { StatusIcon {
icon: "next" icon: "next"
color: Theme.palette.baseColor1 color: Theme.palette.baseColor1

View File

@ -54,8 +54,6 @@ SettingsContentBase {
if(currentIndex == root.networksViewIndex) { if(currentIndex == root.networksViewIndex) {
root.rootStore.backButtonName = qsTr("Wallet") root.rootStore.backButtonName = qsTr("Wallet")
root.sectionTitle = qsTr("Networks") root.sectionTitle = qsTr("Networks")
root.titleRowComponentLoader.sourceComponent = testnetModeSwitchComponent
} }
else if(currentIndex == root.accountViewIndex) { else if(currentIndex == root.accountViewIndex) {
root.rootStore.backButtonName = qsTr("Wallet") root.rootStore.backButtonName = qsTr("Wallet")
@ -118,16 +116,6 @@ SettingsContentBase {
walletStore: root.walletStore walletStore: root.walletStore
} }
Component {
id: testnetModeSwitchComponent
StatusSwitch {
objectName: "testnetModeSwitch"
text: qsTr("Testnet Mode")
checked: walletStore.areTestNetworksEnabled
onClicked: walletStore.toggleTestNetworksEnabled()
}
}
Component { Component {
id: addNewAccountButtonComponent id: addNewAccountButtonComponent
StatusButton { StatusButton {

View File

@ -2,9 +2,14 @@ import QtQuick 2.13
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
import shared.status 1.0 import shared.status 1.0
import shared.popups 1.0
import shared.panels 1.0
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Popups.Dialog 0.1
import utils 1.0 import utils 1.0
import "../../stores" import "../../stores"
@ -21,6 +26,7 @@ Item {
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
width: parent.width width: parent.width
spacing: 0
Repeater { Repeater {
id: layer1List id: layer1List
@ -33,15 +39,20 @@ Item {
} }
delegate: WalletNetworkDelegate { delegate: WalletNetworkDelegate {
network: model network: model
areTestNetworksEnabled: walletStore.areTestNetworksEnabled
} }
} }
Separator {
height: Style.current.padding
}
StatusSectionHeadline { StatusSectionHeadline {
leftPadding: Style.current.padding leftPadding: Style.current.padding
rightPadding: Style.current.padding rightPadding: Style.current.padding
text: qsTr("Layer 2") text: qsTr("Layer 2")
topPadding: Style.current.bigPadding topPadding: Style.current.smallPadding
bottomPadding: Style.current.padding bottomPadding: Style.current.smallPadding
} }
Repeater { Repeater {
@ -55,7 +66,42 @@ Item {
} }
delegate: WalletNetworkDelegate { delegate: WalletNetworkDelegate {
network: model network: model
areTestNetworksEnabled: walletStore.areTestNetworksEnabled
} }
} }
Separator {
height: Style.current.padding
}
StatusSectionHeadline {
leftPadding: Style.current.padding
rightPadding: Style.current.padding
text: qsTr("Advanced")
topPadding: Style.current.smallPadding
bottomPadding: Style.current.smallPadding
}
StatusListItem {
width: parent.width
asset.name: "settings"
asset.color: Theme.palette.warningColor1
asset.bgColor: Theme.palette.warningColor3
title: qsTr("Testnet mode")
subTitle: qsTr("Switch entire Status app to testnet only mode")
onClicked: testnetSwitch.clicked()
components: [
StatusSwitch {
id: testnetSwitch
objectName: "testnetModeSwitch"
checked: walletStore.areTestNetworksEnabled
checkable: false
onClicked: {
checkable = false
Global.openTestnetPopup()
}
}
]
}
} }
} }

View File

@ -535,45 +535,14 @@ Item {
id: testnetBanner id: testnetBanner
objectName: "testnetBanner" objectName: "testnetBanner"
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Testnet mode is enabled. All balances, transactions and dApp interactions will be on testnets.") text: qsTr("Testnet mode enabled. All balances, transactions and dApp interactions will be on testnets.")
buttonText: qsTr("Turn off") buttonText: qsTr("Turn off")
type: ModuleWarning.Danger type: ModuleWarning.Warning
iconName: "warning"
active: appMain.rootStore.profileSectionStore.walletStore.areTestNetworksEnabled active: appMain.rootStore.profileSectionStore.walletStore.areTestNetworksEnabled
onClicked: { onClicked: Global.openTestnetPopup()
testnetBannerDialog.open() onCloseClicked: Global.openTestnetPopup()
}
onCloseClicked: {
testnetBannerDialog.open()
}
StatusDialog {
id: testnetBannerDialog
width: 400
title: qsTr("Turn off Testnet mode")
StatusBaseText {
anchors.fill: parent
text: qsTr("Closing this banner will turn off Testnet mode.\nAll future transactions will be on mainnet or other active networks.")
font.pixelSize: 15
wrapMode: Text.WordWrap
}
footer: StatusDialogFooter {
rightButtons: ObjectModel {
StatusButton {
type: StatusButton.Danger
text: qsTr("Turn off Testnet")
onClicked: {
appMain.rootStore.profileSectionStore.walletStore.toggleTestNetworksEnabled()
testnetBannerDialog.close()
}
}
}
}
}
} }
ModuleWarning { ModuleWarning {

View File

@ -8,6 +8,7 @@ import StatusQ.Core 0.1
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Components 0.1 import StatusQ.Components 0.1
import StatusQ.Popups 0.1 import StatusQ.Popups 0.1
import StatusQ.Core.Theme 0.1
import AppLayouts.Chat.popups 1.0 import AppLayouts.Chat.popups 1.0
import AppLayouts.Profile.popups 1.0 import AppLayouts.Profile.popups 1.0
@ -54,6 +55,7 @@ QtObject {
Global.openDeleteMessagePopup.connect(openDeleteMessagePopup) Global.openDeleteMessagePopup.connect(openDeleteMessagePopup)
Global.openDownloadImageDialog.connect(openDownloadImageDialog) Global.openDownloadImageDialog.connect(openDownloadImageDialog)
Global.leaveCommunityRequested.connect(openLeaveCommunityPopup) Global.leaveCommunityRequested.connect(openLeaveCommunityPopup)
Global.openTestnetPopup.connect(openTestnetPopup)
} }
property var currentPopup property var currentPopup
@ -242,6 +244,10 @@ QtObject {
openPopup(leaveCommunityPopupComponent, {community, communityId, outroMessage}) openPopup(leaveCommunityPopupComponent, {community, communityId, outroMessage})
} }
function openTestnetPopup() {
openPopup(testnetModal)
}
readonly property list<Component> _components: [ readonly property list<Component> _components: [
Component { Component {
id: removeContactConfirmationDialog id: removeContactConfirmationDialog
@ -559,6 +565,29 @@ QtObject {
onClosed: destroy() onClosed: destroy()
} }
},
Component {
id: testnetModal
AlertPopup {
width: 521
readonly property string mainTitle: root.rootStore.profileSectionStore.walletStore.areTestNetworksEnabled ? qsTr("Turn off testnet mode") : qsTr("Turn on testnet mode")
title: mainTitle
alertLabel.textFormat: Text.RichText
alertText: root.rootStore.profileSectionStore.walletStore.areTestNetworksEnabled ?
qsTr("Are you sure you want to turn off %1? All future transactions will be performed on live networks with real funds").arg("<html><span style='font-weight: 500;'>testnet mode</span></html>") :
qsTr("Are you sure you want to turn on %1? In this mode, all blockchain data displayed will come from testnets and all blockchain interactions will be with testnets. Testnet mode switches the entire app to using testnets only. Please switch this mode on only if you know exactly why you need to use it.").arg("<html><span style='font-weight: 500;'>testnet mode</span></html>")
acceptBtnText: mainTitle
acceptBtnType: root.rootStore.profileSectionStore.walletStore.areTestNetworksEnabled ? StatusBaseButton.Type.Normal : StatusBaseButton.Type.Warning
asset.name: "settings"
asset.color: Theme.palette.warningColor1
asset.bgColor: Theme.palette.warningColor3
onAcceptClicked: {
root.rootStore.profileSectionStore.walletStore.toggleTestNetworksEnabled()
Global.displayToastMessage(root.rootStore.profileSectionStore.walletStore.areTestNetworksEnabled ? qsTr("Testnet mode turned on") : qsTr("Testnet mode turned off") , "", "checkmark-circle", false, Constants.ephemeralNotificationType.success, "")
}
onCancelClicked: close()
}
} }
] ]
} }

View File

@ -5,6 +5,7 @@ import QtGraphicalEffects 1.13
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import utils 1.0 import utils 1.0
@ -23,6 +24,7 @@ Item {
property string text: "" property string text: ""
property alias buttonText: button.text property alias buttonText: button.text
property alias closeBtnVisible: closeImg.visible property alias closeBtnVisible: closeImg.visible
property string iconName
signal clicked() signal clicked()
signal closeClicked() signal closeClicked()
@ -129,6 +131,16 @@ Item {
spacing: 12 spacing: 12
anchors.centerIn: parent anchors.centerIn: parent
StatusRoundIcon {
Layout.preferredHeight: 16
Layout.preferredWidth: 16
Layout.rightMargin: -8
visible: !!root.iconName
asset.name: root.iconName
asset.bgColor: Theme.palette.indirectColor1
asset.color: content.baseColor
}
StatusBaseText { StatusBaseText {
text: root.text text: root.text
font.pixelSize: 13 font.pixelSize: 13

View File

@ -7,8 +7,7 @@ import StatusQ.Core 0.1
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Popups.Dialog 0.1 import StatusQ.Popups.Dialog 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import AppLayouts.Communities.panels 1.0
import utils 1.0 import utils 1.0
@ -17,6 +16,19 @@ StatusDialog {
property alias acceptBtnText: acceptBtn.text property alias acceptBtnText: acceptBtn.text
property alias alertText: contentTextItem.text property alias alertText: contentTextItem.text
property alias alertLabel: contentTextItem
property int acceptBtnType: StatusBaseButton.Type.Danger
property StatusAssetSettings asset: StatusAssetSettings {
width: 24
height: 24
rotation: 0
color: Theme.palette.primaryColor1
bgWidth: 40
bgHeight: 40
bgColor: Theme.palette.primaryColor3
bgRadius: bgWidth / 2
}
signal acceptClicked signal acceptClicked
signal cancelClicked signal cancelClicked
@ -32,6 +44,18 @@ StatusDialog {
lineHeight: 1.2 lineHeight: 1.2
} }
header: StatusDialogHeader {
visible: root.title || root.subtitle
headline.title: root.title
headline.subtitle: root.subtitle
actions.closeButton.onClicked: root.close()
leftComponent: StatusRoundIcon {
width: visible? implicitWidth: 0
visible: !!root.asset.name
asset: root.asset
}
}
footer: StatusDialogFooter { footer: StatusDialogFooter {
spacing: Style.current.padding spacing: Style.current.padding
rightButtons: ObjectModel { rightButtons: ObjectModel {
@ -49,7 +73,7 @@ StatusDialog {
StatusButton { StatusButton {
id: acceptBtn id: acceptBtn
type: StatusBaseButton.Type.Danger type: root.acceptBtnType
onClicked: { onClicked: {
root.acceptClicked() root.acceptClicked()

View File

@ -29,3 +29,4 @@ RemoveAccountConfirmationPopup 1.0 RemoveAccountConfirmationPopup.qml
RenameGroupPopup 1.0 RenameGroupPopup.qml RenameGroupPopup 1.0 RenameGroupPopup.qml
DeleteMessageConfirmationPopup 1.0 DeleteMessageConfirmationPopup.qml DeleteMessageConfirmationPopup 1.0 DeleteMessageConfirmationPopup.qml
UserAgreementPopup 1.0 UserAgreementPopup.qml UserAgreementPopup 1.0 UserAgreementPopup.qml
AlertPopup 1.0 AlertPopup.qml

View File

@ -64,6 +64,8 @@ QtObject {
signal playNotificationSound() signal playNotificationSound()
signal playErrorSound() signal playErrorSound()
signal openTestnetPopup()
function openProfilePopup(publicKey, parentPopup, cb) { function openProfilePopup(publicKey, parentPopup, cb) {
root.openProfilePopupRequested(publicKey, parentPopup, cb) root.openProfilePopupRequested(publicKey, parentPopup, cb)
} }

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit 9ee523be99483a14f83c3e7a74220d5695ee755a Subproject commit 104d9c8ff6b4f7f02bfd04d053a948b84f230fda