List custom networks and allow selecting them

This commit is contained in:
Richard Ramos 2021-01-08 18:10:31 -04:00 committed by Iuri Matias
parent 0acc959e4d
commit 6b1cef9235
9 changed files with 291 additions and 202 deletions

View File

@ -0,0 +1,50 @@
import NimQml
import Tables
import json, sequtils, sugar
import ../../../status/libstatus/settings
import ../../../status/libstatus/types
type
CustomNetworkRoles {.pure.} = enum
Id = UserRole + 1,
Name = UserRole + 2
const defaultNetworks = @["mainnet_rpc", "testnet_rpc", "rinkeby_rpc", "goerli_rpc", "xdai_rpc", "poa_rpc" ]
QtObject:
type CustomNetworkList* = ref object of QAbstractListModel
proc setup(self: CustomNetworkList) = self.QAbstractListModel.setup
proc delete(self: CustomNetworkList) =
self.QAbstractListModel.delete
proc newCustomNetworkList*(): CustomNetworkList =
new(result, delete)
result.setup
method rowCount(self: CustomNetworkList, index: QModelIndex = nil): int =
let networks = getSetting[JsonNode](Setting.Networks_Networks)
return networks.getElems().filterIt(it["id"].getStr() notin defaultNetworks).len
method data(self: CustomNetworkList, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
let networks = getSetting[JsonNode](Setting.Networks_Networks).getElems().filterIt(it["id"].getStr() notin defaultNetworks)
if index.row < 0 or index.row >= networks.len:
return
let network = networks[index.row]
case role.CustomNetworkRoles:
of CustomNetworkRoles.Id: result = newQVariant(network["id"].getStr)
of CustomNetworkRoles.Name: result = newQVariant(network["name"].getStr)
method roleNames(self: CustomNetworkList): Table[int, string] =
{
CustomNetworkRoles.Id.int:"customNetworkId",
CustomNetworkRoles.Name.int:"name",
}.toTable
proc forceReload*(self: CustomNetworkList) =
self.beginResetModel()
self.endResetModel()

View File

@ -1,6 +1,7 @@
import NimQml, chronicles
import ../../../status/status
import ../../../status/network
import custom_networks
logScope:
topics = "network-view"
@ -9,16 +10,19 @@ QtObject:
type NetworkView* = ref object of QObject
status: Status
network: string
customNetworkList*: CustomNetworkList
proc setup(self: NetworkView) =
self.QObject.setup
proc delete*(self: NetworkView) =
self.customNetworkList.delete
self.QObject.delete
proc newNetworkView*(status: Status): NetworkView =
new(result, delete)
result.status = status
result.customNetworkList = newCustomNetworkList()
result.setup
proc networkChanged*(self: NetworkView) {.signal.}
@ -36,7 +40,7 @@ QtObject:
proc setNetworkAndPersist*(self: NetworkView, network: string) {.slot.} =
self.network = network
self.networkChanged()
self.status.accounts.changeNetwork(self.status.fleet.config, network)
self.status.accounts.changeNetwork(self.status.fleet.config, network) ###############################
quit(QuitSuccess) # quits the app TODO: change this to logout instead when supported
QtProperty[QVariant] current:
@ -47,3 +51,11 @@ QtObject:
proc add*(self: NetworkView, name: string, endpoint: string, networkId: int, networkType: string) {.slot.} =
self.status.network.addNetwork(name, endpoint, networkId, networkType)
proc getCustomNetworkList(self: NetworkView): QVariant {.slot.} =
return newQVariant(self.customNetworkList)
QtProperty[QVariant] customNetworkList:
read = getCustomNetworkList
proc reloadCustomNetworks(self: NetworkView) {.slot.} =
self.customNetworkList.forceReload()

View File

@ -5,6 +5,8 @@ import ../../status/accounts as status_accounts
import ../../status/stickers
import ../../status/libstatus/accounts/constants as accountConstants
import ../../status/libstatus/tokens
import ../../status/libstatus/types
import ../../status/libstatus/settings
import ../../status/libstatus/wallet as status_wallet
import ../../status/libstatus/utils as status_utils
import ../../status/ens as status_ens
@ -91,3 +93,6 @@ QtObject:
proc generateIdenticon*(self: UtilsView, pk: string): string {.slot.} =
result = status_accounts.generateIdenticon(pk)
proc getNetworkName*(self: UtilsView): string {.slot.} =
getCurrentNetworkDetails().name

View File

@ -1,4 +1,4 @@
import options, chronicles, json
import options, chronicles, json, json_serialization, sequtils, sugar
import libstatus/accounts as status_accounts
import libstatus/settings as status_settings
import libstatus/types
@ -63,7 +63,11 @@ proc changeNetwork*(self: AccountModel, fleetConfig: FleetConfig, network: strin
# 2. update node config setting
let installationId = status_settings.getSetting[string](Setting.InstallationId)
let updatedNodeConfig = status_accounts.getNodeConfig(fleetConfig, installationId, network)
let networks = getSetting[JsonNode](Setting.Networks_Networks)
let networkData = networks.getElems().find((n:JsonNode) => n["id"].getStr() == network)
let updatedNodeConfig = status_accounts.getNodeConfig(fleetConfig, installationId, networkData)
statusGoResult = status_settings.saveSetting(Setting.NodeConfig, updatedNodeConfig)
if statusGoResult.error != "":
error "Error saving updated node config", msg=statusGoResult.error

View File

@ -12,8 +12,7 @@ proc getNetworkConfig(currentNetwork: string): JsonNode =
result = constants.DEFAULT_NETWORKS.first("id", currentNetwork)
proc getNodeConfig*(fleetConfig: FleetConfig, installationId: string, currentNetwork: string = constants.DEFAULT_NETWORK_NAME, fleet: Fleet = Fleet.PROD): JsonNode =
let networkConfig = getNetworkConfig(currentNetwork)
proc getNodeConfig*(fleetConfig: FleetConfig, installationId: string, networkConfig: JsonNode, fleet: Fleet = Fleet.PROD): JsonNode =
let upstreamUrl = networkConfig["config"]["UpstreamConfig"]["URL"]
var newDataDir = networkConfig["config"]["DataDir"].getStr
newDataDir.removeSuffix("_rpc")
@ -32,6 +31,9 @@ proc getNodeConfig*(fleetConfig: FleetConfig, installationId: string, currentNet
result["ShhextConfig"]["InstallationID"] = newJString(installationId)
result["ListenAddr"] = if existsEnv("STATUS_PORT"): newJString("0.0.0.0:" & $getEnv("STATUS_PORT")) else: newJString("0.0.0.0:30305")
proc getNodeConfig*(fleetConfig: FleetConfig, installationId: string, currentNetwork: string = constants.DEFAULT_NETWORK_NAME, fleet: Fleet = Fleet.PROD): JsonNode =
let networkConfig = getNetworkConfig(currentNetwork)
result = getNodeConfig(fleetConfig, installationId, networkConfig, fleet)
proc hashPassword*(password: string): string =
result = "0x" & $keccak_256.digest(password)

View File

@ -51,4 +51,4 @@ proc addNetwork*(self: NetworkModel, name: string, endpoint: string, networkId:
}
}
})
discard saveSetting(Setting.Networks_Networks, $networks)
discard saveSetting(Setting.Networks_Networks, networks)

View File

@ -127,7 +127,7 @@ Item {
}
StyledText {
text: Utils.getNetworkName(profileModel.network.current)
text: utilsModel.getNetworkName()
font.pixelSize: 15
anchors.right: caret3.left
anchors.rightMargin: Style.current.padding

View File

@ -7,6 +7,7 @@ import "../../../../shared/status"
RowLayout {
property string network: ""
property string networkName: ""
property string newNetwork: ""
ConfirmationDialog {
@ -21,7 +22,7 @@ RowLayout {
width: parent.width
StyledText {
text: Utils.getNetworkName(network)
text: networkName == "" ? Utils.getNetworkName(network) : networkName
font.pixelSize: 15
}
StatusRadioButton {

View File

@ -11,248 +11,263 @@ ModalPopup {
property string newNetwork: "";
Column {
id: column
spacing: Style.current.padding
ScrollView {
id: svNetworks
width: parent.width
height: 300
clip: true
ButtonGroup { id: networkSettings }
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
Item {
id: addNetwork
Column {
id: column
spacing: Style.current.padding
width: parent.width
height: addButton.height
StatusRoundButton {
id: addButton
icon.name: "plusSign"
size: "medium"
anchors.verticalCenter: parent.verticalCenter
}
ButtonGroup { id: networkSettings }
ButtonGroup {
id: networkChainGroup
}
Item {
id: addNetwork
width: parent.width
height: addButton.height
StyledText {
id: usernameText
text: qsTr("Add network")
color: Style.current.blue
anchors.left: addButton.right
anchors.leftMargin: Style.current.padding
anchors.verticalCenter: addButton.verticalCenter
font.pixelSize: 15
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: addNetworkPopup.open()
}
ModalPopup {
id: addNetworkPopup
title: qsTr("Add network")
height: 600
property string nameValidationError: ""
property string rpcValidationError: ""
property int networkId: 1;
property string networkType: Constants.networkMainnet
function validate() {
nameValidationError = ""
rpcValidationError = ""
if (nameInput.text === "") {
nameValidationError = qsTr("You need to enter a name")
}
if (rpcInput.text === "") {
rpcValidationError = qsTr("You need to enter the RPC endpoint URL")
} else if(!Utils.isURL(rpcInput.text)) {
rpcValidationError = qsTr("Invalid URL")
}
return !nameValidationError && !rpcValidationError
StatusRoundButton {
id: addButton
icon.name: "plusSign"
size: "medium"
anchors.verticalCenter: parent.verticalCenter
}
onOpened: {
nameInput.text = "";
rpcInput.text = "";
mainnetRadioBtn.checked = true;
addNetworkPopup.networkId = 1;
addNetworkPopup.networkType = Constants.networkMainnet;
nameValidationError = "";
rpcValidationError = "";
ButtonGroup {
id: networkChainGroup
}
footer: StyledButton {
anchors.right: parent.right
anchors.rightMargin: Style.current.smallPadding
label: qsTr("Save")
anchors.bottom: parent.bottom
disabled: nameInput.text == "" || rpcInput.text == ""
onClicked: {
if (!addNetworkPopup.validate()) {
return;
}
profileModel.network.add(nameInput.text, rpcInput.text, addNetworkPopup.networkId, addNetworkPopup.networkType)
addNetworkPopup.close()
}
StyledText {
id: usernameText
text: qsTr("Add network")
color: Style.current.blue
anchors.left: addButton.right
anchors.leftMargin: Style.current.padding
anchors.verticalCenter: addButton.verticalCenter
font.pixelSize: 15
}
Input {
id: nameInput
label: qsTr("Name")
placeholderText: qsTr("Specify a name")
validationError: addNetworkPopup.nameValidationError
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: addNetworkPopup.open()
}
Input {
id: rpcInput
label: qsTr("RPC URL")
placeholderText: qsTr("Specify a RPC URL")
validationError: addNetworkPopup.rpcValidationError
anchors.top: nameInput.bottom
anchors.topMargin: Style.current.bigPadding
}
ModalPopup {
id: addNetworkPopup
title: qsTr("Add network")
height: 600
StatusSectionHeadline {
id: networkChainHeadline
text: qsTr("Network chain")
anchors.top: rpcInput.bottom
anchors.topMargin: Style.current.bigPadding
}
property string nameValidationError: ""
property string rpcValidationError: ""
property int networkId: 1;
property string networkType: Constants.networkMainnet
Column {
spacing: Style.current.padding
anchors.top: networkChainHeadline.bottom
anchors.topMargin: Style.current.smallPadding
anchors.left: parent.left
anchors.right: parent.right
function validate() {
nameValidationError = ""
rpcValidationError = ""
RowLayout {
width: parent.width
StyledText {
text: qsTr("Main network")
font.pixelSize: 15
if (nameInput.text === "") {
nameValidationError = qsTr("You need to enter a name")
}
StatusRadioButton {
id: mainnetRadioBtn
Layout.alignment: Qt.AlignRight
ButtonGroup.group: networkChainGroup
rightPadding: 0
checked: true
onClicked: {
addNetworkPopup.networkId = 1;
addNetworkPopup.networkType = Constants.networkMainnet;
if (rpcInput.text === "") {
rpcValidationError = qsTr("You need to enter the RPC endpoint URL")
} else if(!Utils.isURL(rpcInput.text)) {
rpcValidationError = qsTr("Invalid URL")
}
return !nameValidationError && !rpcValidationError
}
onOpened: {
nameInput.text = "";
rpcInput.text = "";
mainnetRadioBtn.checked = true;
addNetworkPopup.networkId = 1;
addNetworkPopup.networkType = Constants.networkMainnet;
nameValidationError = "";
rpcValidationError = "";
}
footer: StyledButton {
anchors.right: parent.right
anchors.rightMargin: Style.current.smallPadding
label: qsTr("Save")
anchors.bottom: parent.bottom
disabled: nameInput.text == "" || rpcInput.text == ""
onClicked: {
if (!addNetworkPopup.validate()) {
return;
}
profileModel.network.add(nameInput.text, rpcInput.text, addNetworkPopup.networkId, addNetworkPopup.networkType)
profileModel.network.reloadCustomNetworks();
addNetworkPopup.close()
}
}
Input {
id: nameInput
label: qsTr("Name")
placeholderText: qsTr("Specify a name")
validationError: addNetworkPopup.nameValidationError
}
Input {
id: rpcInput
label: qsTr("RPC URL")
placeholderText: qsTr("Specify a RPC URL")
validationError: addNetworkPopup.rpcValidationError
anchors.top: nameInput.bottom
anchors.topMargin: Style.current.bigPadding
}
StatusSectionHeadline {
id: networkChainHeadline
text: qsTr("Network chain")
anchors.top: rpcInput.bottom
anchors.topMargin: Style.current.bigPadding
}
Column {
spacing: Style.current.padding
anchors.top: networkChainHeadline.bottom
anchors.topMargin: Style.current.smallPadding
anchors.left: parent.left
anchors.right: parent.right
RowLayout {
width: parent.width
StyledText {
text: qsTr("Main network")
font.pixelSize: 15
}
StatusRadioButton {
id: mainnetRadioBtn
Layout.alignment: Qt.AlignRight
ButtonGroup.group: networkChainGroup
rightPadding: 0
checked: true
onClicked: {
addNetworkPopup.networkId = 1;
addNetworkPopup.networkType = Constants.networkMainnet;
}
}
}
}
RowLayout {
width: parent.width
StyledText {
text: qsTr("Ropsten test network")
font.pixelSize: 15
}
StatusRadioButton {
id: ropstenRadioBtn
Layout.alignment: Qt.AlignRight
ButtonGroup.group: networkChainGroup
rightPadding: 0
onClicked: {
addNetworkPopup.networkId = 3;
addNetworkPopup.networkType = Constants.networkRopsten;
RowLayout {
width: parent.width
StyledText {
text: qsTr("Ropsten test network")
font.pixelSize: 15
}
StatusRadioButton {
id: ropstenRadioBtn
Layout.alignment: Qt.AlignRight
ButtonGroup.group: networkChainGroup
rightPadding: 0
onClicked: {
addNetworkPopup.networkId = 3;
addNetworkPopup.networkType = Constants.networkRopsten;
}
}
}
}
RowLayout {
width: parent.width
StyledText {
text: qsTr("Rinkeby test network")
font.pixelSize: 15
}
StatusRadioButton {
id: rinkebyRadioBtn
Layout.alignment: Qt.AlignRight
ButtonGroup.group: networkChainGroup
rightPadding: 0
onClicked: {
addNetworkPopup.networkId = 4;
addNetworkPopup.networkType = Constants.networkRinkeby;
RowLayout {
width: parent.width
StyledText {
text: qsTr("Rinkeby test network")
font.pixelSize: 15
}
StatusRadioButton {
id: rinkebyRadioBtn
Layout.alignment: Qt.AlignRight
ButtonGroup.group: networkChainGroup
rightPadding: 0
onClicked: {
addNetworkPopup.networkId = 4;
addNetworkPopup.networkType = Constants.networkRinkeby;
}
}
}
}
RowLayout {
width: parent.width
StyledText {
text: qsTr("Custom")
font.pixelSize: 15
}
StatusRadioButton {
id: customRadioBtn
Layout.alignment: Qt.AlignRight
ButtonGroup.group: networkChainGroup
rightPadding: 0
onClicked: {
addNetworkPopup.networkId = 55; // TODO
addNetworkPopup.networkType = "";
RowLayout {
width: parent.width
StyledText {
text: qsTr("Custom")
font.pixelSize: 15
}
StatusRadioButton {
id: customRadioBtn
Layout.alignment: Qt.AlignRight
ButtonGroup.group: networkChainGroup
rightPadding: 0
onClicked: {
addNetworkPopup.networkId = 55; // TODO
addNetworkPopup.networkType = "";
}
}
}
}
}
}
}
StatusSectionHeadline {
text: qsTr("Main networks")
}
StatusSectionHeadline {
text: qsTr("Main networks")
}
NetworkRadioSelector {
network: Constants.networkMainnet
}
NetworkRadioSelector {
network: Constants.networkMainnet
}
NetworkRadioSelector {
network: Constants.networkPOA
}
NetworkRadioSelector {
network: Constants.networkPOA
}
NetworkRadioSelector {
network: Constants.networkXDai
}
NetworkRadioSelector {
network: Constants.networkXDai
}
StatusSectionHeadline {
text: qsTr("Test networks")
}
StatusSectionHeadline {
text: qsTr("Test networks")
}
NetworkRadioSelector {
network: Constants.networkGoerli
}
NetworkRadioSelector {
network: Constants.networkGoerli
}
NetworkRadioSelector {
network: Constants.networkRinkeby
}
NetworkRadioSelector {
network: Constants.networkRinkeby
}
NetworkRadioSelector {
network: Constants.networkRopsten
}
NetworkRadioSelector {
network: Constants.networkRopsten
}
StatusSectionHeadline {
text: qsTr("Custom Networks")
}
StatusSectionHeadline {
text: qsTr("Custom Networks")
}
NetworkRadioSelector {
network: "SOME NETWORK"
Repeater {
model: profileModel.network.customNetworkList
delegate: NetworkRadioSelector {
networkName: name
network: customNetworkId
}
}
}
}
StyledText {
anchors.top: column.bottom
anchors.top: svNetworks.bottom
anchors.topMargin: Style.current.padding
//% "Under development\nNOTE: You will be logged out and all installed\nsticker packs will be removed and will\nneed to be reinstalled. Purchased sticker\npacks will not need to be re-purchased."
text: qsTrId("under-development-nnote--you-will-be-logged-out-and-all-installed-nsticker-packs-will-be-removed-and-will-nneed-to-be-reinstalled--purchased-sticker-npacks-will-not-need-to-be-re-purchased-")