fix(@desktop/wallet): Edit Networks: Add warning when failover and main rpc are the same

closes #11551
This commit is contained in:
Khushboo Mehta 2023-09-04 17:36:28 +02:00 committed by Khushboo-dev-cpp
parent 428ff4d089
commit a8cb40809c
12 changed files with 97 additions and 67 deletions

View File

@ -36,7 +36,7 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_WALLET_ACCOUNT_CHAIN_ID_FOR_URL_FETCHED) do(e: Args): self.events.on(SIGNAL_WALLET_ACCOUNT_CHAIN_ID_FOR_URL_FETCHED) do(e: Args):
let args = ChainIdForUrlArgs(e) let args = ChainIdForUrlArgs(e)
self.delegate.chainIdFetchedForUrl(args.url, args.chainId, args.success) self.delegate.chainIdFetchedForUrl(args.url, args.chainId, args.success, args.isMainUrl)
self.events.on(SIGNAL_NETWORK_ENDPOINT_UPDATED) do(e: Args): self.events.on(SIGNAL_NETWORK_ENDPOINT_UPDATED) do(e: Args):
self.delegate.refreshNetworks() self.delegate.refreshNetworks()
@ -50,8 +50,8 @@ proc areTestNetworksEnabled*(self: Controller): bool =
proc toggleTestNetworksEnabled*(self: Controller) = proc toggleTestNetworksEnabled*(self: Controller) =
self.walletAccountService.toggleTestNetworksEnabled() self.walletAccountService.toggleTestNetworksEnabled()
proc fetchChainIdForUrl*(self: Controller, url: string) = proc fetchChainIdForUrl*(self: Controller, url: string, isMainUrl: bool) =
self.walletAccountService.fetchChainIdForUrl(url) self.walletAccountService.fetchChainIdForUrl(url, isMainUrl)
proc updateNetworkEndPointValues*(self: Controller, chainId: int, newMainRpcInput, newFailoverRpcUrl: string) = proc updateNetworkEndPointValues*(self: Controller, chainId: int, newMainRpcInput, newFailoverRpcUrl: string) =
self.networkService.updateNetworkEndPointValues(chainId, newMainRpcInput, newFailoverRpcUrl) self.networkService.updateNetworkEndPointValues(chainId, newMainRpcInput, newFailoverRpcUrl)

View File

@ -31,8 +31,8 @@ method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} =
method updateNetworkEndPointValues*(self: AccessInterface, chainId: int, newMainRpcInput, newFailoverRpcUrl: string) {.base.} = method updateNetworkEndPointValues*(self: AccessInterface, chainId: int, newMainRpcInput, newFailoverRpcUrl: string) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method fetchChainIdForUrl*(self: AccessInterface, url: string) {.base.} = method fetchChainIdForUrl*(self: AccessInterface, url: string, isMainUrl: bool) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method chainIdFetchedForUrl*(self: AccessInterface, url: string, chainId: int, success: bool) {.base.} = method chainIdFetchedForUrl*(self: AccessInterface, url: string, chainId: int, success: bool, isMainUrl: bool) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")

View File

@ -103,8 +103,8 @@ method toggleTestNetworksEnabled*(self: Module) =
method updateNetworkEndPointValues*(self: Module, chainId: int, newMainRpcInput, newFailoverRpcUrl: string) = method updateNetworkEndPointValues*(self: Module, chainId: int, newMainRpcInput, newFailoverRpcUrl: string) =
self.controller.updateNetworkEndPointValues(chainId, newMainRpcInput, newFailoverRpcUrl) self.controller.updateNetworkEndPointValues(chainId, newMainRpcInput, newFailoverRpcUrl)
method fetchChainIdForUrl*(self: Module, url: string) = method fetchChainIdForUrl*(self: Module, url: string, isMainUrl: bool) =
self.controller.fetchChainIdForUrl(url) self.controller.fetchChainIdForUrl(url, isMainUrl)
method chainIdFetchedForUrl*(self: Module, url: string, chainId: int, success: bool) = method chainIdFetchedForUrl*(self: Module, url: string, chainId: int, success: bool, isMainUrl: bool) =
self.view.chainIdFetchedForUrl(url, chainId, success) self.view.chainIdFetchedForUrl(url, chainId, success, isMainUrl)

View File

@ -76,7 +76,7 @@ QtObject:
proc updateNetworkEndPointValues*(self: View, chainId: int, newMainRpcInput: string, newFailoverRpcUrl: string) {.slot.} = proc updateNetworkEndPointValues*(self: View, chainId: int, newMainRpcInput: string, newFailoverRpcUrl: string) {.slot.} =
self.delegate.updateNetworkEndPointValues(chainId, newMainRpcInput, newFailoverRpcUrl) self.delegate.updateNetworkEndPointValues(chainId, newMainRpcInput, newFailoverRpcUrl)
proc fetchChainIdForUrl*(self: View, url: string) {.slot.} = proc fetchChainIdForUrl*(self: View, url: string, isMainUrl: bool) {.slot.} =
self.delegate.fetchChainIdForUrl(url) self.delegate.fetchChainIdForUrl(url, isMainUrl)
proc chainIdFetchedForUrl*(self: View, url: string, chainId: int, success: bool) {.signal.} proc chainIdFetchedForUrl*(self: View, url: string, chainId: int, success: bool, isMainUrl: bool) {.signal.}

View File

@ -164,6 +164,7 @@ const deleteKeycardAccountsTask*: Task = proc(argEncoded: string) {.gcsafe, nimc
type type
FetchChainIdForUrlTaskArg* = ref object of QObjectTaskArg FetchChainIdForUrlTaskArg* = ref object of QObjectTaskArg
url: string url: string
isMainUrl: bool
const fetchChainIdForUrlTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = const fetchChainIdForUrlTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[FetchChainIdForUrlTaskArg](argEncoded) let arg = decode[FetchChainIdForUrlTaskArg](argEncoded)
@ -172,14 +173,16 @@ const fetchChainIdForUrlTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall
arg.finish(%*{ arg.finish(%*{
"success": true, "success": true,
"chainId": response.result.getInt, "chainId": response.result.getInt,
"url": arg.url "url": arg.url,
"isMainUrl": arg.isMainUrl
}) })
except Exception as e: except Exception as e:
error "error when fetching chaind id from url: ", message = e.msg error "error when fetching chaind id from url: ", message = e.msg
arg.finish(%*{ arg.finish(%*{
"success": false, "success": false,
"chainId": -1, "chainId": -1,
"url": arg.url "url": arg.url,
"isMainUrl": arg.isMainUrl
}) })
################################################# #################################################
@ -204,4 +207,4 @@ const migrateNonProfileKeycardKeypairToAppTask*: Task = proc(argEncoded: string)
responseJson["success"] = %* success responseJson["success"] = %* success
except Exception as e: except Exception as e:
error "error migrating a non profile keycard keypair: ", message = e.msg error "error migrating a non profile keycard keypair: ", message = e.msg
arg.finish(responseJson) arg.finish(responseJson)

View File

@ -731,14 +731,16 @@ proc onFetchChainIdForUrl*(self: Service, jsonString: string) {.slot.} =
chainId: response{"chainId"}.getInt, chainId: response{"chainId"}.getInt,
success: response{"success"}.getBool, success: response{"success"}.getBool,
url: response{"url"}.getStr, url: response{"url"}.getStr,
isMainUrl: response{"isMainUrl"}.getBool
)) ))
proc fetchChainIdForUrl*(self: Service, url: string) = proc fetchChainIdForUrl*(self: Service, url: string, isMainUrl: bool) =
let arg = FetchChainIdForUrlTaskArg( let arg = FetchChainIdForUrlTaskArg(
tptr: cast[ByteAddress](fetchChainIdForUrlTask), tptr: cast[ByteAddress](fetchChainIdForUrlTask),
vptr: cast[ByteAddress](self.vptr), vptr: cast[ByteAddress](self.vptr),
slot: "onFetchChainIdForUrl", slot: "onFetchChainIdForUrl",
url: url url: url,
isMainUrl: isMainUrl
) )
self.threadpool.start(arg) self.threadpool.start(arg)
@ -758,4 +760,4 @@ proc importPartiallyOperableAccounts(self: Service, keyUid: string, password: st
## Whenever user provides a password/pin we need to make all partially operable accounts (if any exists) a fully operable. ## Whenever user provides a password/pin we need to make all partially operable accounts (if any exists) a fully operable.
if keyUid != singletonInstance.userProfile.getKeyUid(): if keyUid != singletonInstance.userProfile.getKeyUid():
return return
self.makePartiallyOperableAccoutsFullyOperable(password, not singletonInstance.userProfile.getIsKeycardUser()) self.makePartiallyOperableAccoutsFullyOperable(password, not singletonInstance.userProfile.getIsKeycardUser())

View File

@ -81,3 +81,4 @@ type ChainIdForUrlArgs* = ref object of Args
chainId*: int chainId*: int
success*: bool success*: bool
url*: string url*: string
isMainUrl*: bool

View File

@ -67,7 +67,7 @@ SplitView {
signal urlVerified(string url, int status) signal urlVerified(string url, int status)
property string url property string url
function evaluateRpcEndPoint(url) { function evaluateRpcEndPoint(url, isMainUrl) {
networkModule.url = url networkModule.url = url
d.timer.restart() d.timer.restart()
} }

View File

@ -72,8 +72,8 @@ QtObject {
root.walletModule.runKeypairImportPopup(keyUid, mode) root.walletModule.runKeypairImportPopup(keyUid, mode)
} }
function evaluateRpcEndPoint(url) { function evaluateRpcEndPoint(url, isMainUrl) {
return networksModule.fetchChainIdForUrl(url) return networksModule.fetchChainIdForUrl(url, isMainUrl)
} }
function updateNetworkEndPointValues(chainId, newMainRpcInput, newFailoverRpcUrl) { function updateNetworkEndPointValues(chainId, newMainRpcInput, newFailoverRpcUrl) {

View File

@ -152,7 +152,7 @@ SettingsContentBase {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
networksModule: root.walletStore.networksModule networksModule: root.walletStore.networksModule
onEvaluateRpcEndPoint: root.walletStore.evaluateRpcEndPoint(url) onEvaluateRpcEndPoint: root.walletStore.evaluateRpcEndPoint(url, isMainUrl)
onUpdateNetworkValues: { onUpdateNetworkValues: {
root.walletStore.updateNetworkEndPointValues(chainId, newMainRpcInput, newFailoverRpcUrl) root.walletStore.updateNetworkEndPointValues(chainId, newMainRpcInput, newFailoverRpcUrl)
stackContainer.currentIndex = networksViewIndex stackContainer.currentIndex = networksViewIndex

View File

@ -13,7 +13,7 @@ ColumnLayout {
property var network property var network
property var networksModule property var networksModule
signal evaluateRpcEndPoint(string url) signal evaluateRpcEndPoint(string url, bool isMainUrl)
signal updateNetworkValues(int chainId, string newMainRpcInput, string newFailoverRpcUrl) signal updateNetworkValues(int chainId, string newMainRpcInput, string newFailoverRpcUrl)
enum EvaluationState { enum EvaluationState {
@ -21,29 +21,28 @@ ColumnLayout {
Pending, Pending,
Verified, Verified,
InvalidURL, InvalidURL,
PingUnsuccessful PingUnsuccessful,
SameAsOther
} }
QtObject { QtObject {
id: d id: d
property int evaluationStatus: EditNetworkForm.UnTouched property int evaluationStatusMainRpc: EditNetworkForm.UnTouched
property int evaluationStatusFallBackRpc: EditNetworkForm.UnTouched property int evaluationStatusFallBackRpc: EditNetworkForm.UnTouched
property var evaluateRpcEndPoint: Backpressure.debounce(root, 400, function (value) { property var evaluateRpcEndPoint: Backpressure.debounce(root, 400, function (value, isMainUrl) {
if(!Utils.isURL(value)) { if(!Utils.isURL(value)) {
if(value === mainRpcInput.text) { if(isMainUrl)
d.evaluationStatus = EditNetworkForm.InvalidURL d.evaluationStatusMainRpc = EditNetworkForm.InvalidURL
} else
else if(value === failoverRpcUrlInput.text) {
d.evaluationStatusFallBackRpc = EditNetworkForm.InvalidURL d.evaluationStatusFallBackRpc = EditNetworkForm.InvalidURL
}
return return
} }
root.evaluateRpcEndPoint(value) root.evaluateRpcEndPoint(value, isMainUrl)
}) })
function revertValues() { function revertValues() {
warningCheckbox.checked = false warningCheckbox.checked = false
d.evaluationStatus = EditNetworkForm.UnTouched d.evaluationStatusMainRpc = EditNetworkForm.UnTouched
d.evaluationStatusFallBackRpc = EditNetworkForm.UnTouched d.evaluationStatusFallBackRpc = EditNetworkForm.UnTouched
if(!!network) { if(!!network) {
mainRpcInput.text = network.rpcURL mainRpcInput.text = network.rpcURL
@ -61,6 +60,8 @@ ColumnLayout {
return qsTr("RPC appears to be either offline or this is not a valid JSON RPC endpoint URL") return qsTr("RPC appears to be either offline or this is not a valid JSON RPC endpoint URL")
case EditNetworkForm.Verified: case EditNetworkForm.Verified:
return qsTr("RPC successfully reached") return qsTr("RPC successfully reached")
case EditNetworkForm.SameAsOther:
return qsTr("Main and failover JSON RPC URLs are the same")
default: return "" default: return ""
} }
} }
@ -70,17 +71,20 @@ ColumnLayout {
Connections { Connections {
target: networksModule target: networksModule
function onChainIdFetchedForUrl(url, chainId, success) { function onChainIdFetchedForUrl(url, chainId, success, isMainUrl) {
let status = EditNetworkForm.PingUnsuccessful let status = EditNetworkForm.PingUnsuccessful
if(success) { if(success) {
status = EditNetworkForm.Verified if((isMainUrl && url === network.fallbackURL) ||
} (!isMainUrl && url === network.rpcURL)) {
if(url === mainRpcInput.text) { status = EditNetworkForm.SameAsOther
d.evaluationStatus = status }
} else
else if(url === failoverRpcUrlInput.text) { status = EditNetworkForm.Verified
d.evaluationStatusFallBackRpc = status
} }
if(isMainUrl)
d.evaluationStatusMainRpc = status
else
d.evaluationStatusFallBackRpc = status
} }
} }
@ -135,26 +139,37 @@ ColumnLayout {
if (!network) { if (!network) {
return "" return ""
} }
if (network.originalRpcURL == network.rpcURL) { if (network.originalRpcURL === network.rpcURL) {
return network.rpcURL.replace(/(.*\/).*/, '$1') return network.rpcURL.replace(/(.*\/).*/, '$1')
} }
return network.rpcURL return network.rpcURL
} }
onTextChanged: { onTextChanged: {
if(!!text && text !== network.rpcURL.replace(/(.*\/).*/, '$1')) { if(!!text &&
d.evaluationStatus = EditNetworkForm.Pending (network.originalRpcURL === network.rpcURL && text !== network.rpcURL.replace(/(.*\/).*/, '$1')) ||
Qt.callLater(d.evaluateRpcEndPoint, text); (network.originalRpcURL !== network.rpcURL && text !== network.rpcURL)) {
d.evaluationStatusMainRpc = EditNetworkForm.Pending
Qt.callLater(d.evaluateRpcEndPoint, text, true);
} }
} }
errorMessageCmp.horizontalAlignment: d.evaluationStatus === EditNetworkForm.Pending || errorMessageCmp.horizontalAlignment: d.evaluationStatusMainRpc === EditNetworkForm.Pending ||
d.evaluationStatus === EditNetworkForm.Verified ? d.evaluationStatusMainRpc === EditNetworkForm.Verified ||
d.evaluationStatusMainRpc === EditNetworkForm.SameAsOther ?
Text.AlignLeft: Text.AlignRight Text.AlignLeft: Text.AlignRight
errorMessageCmp.visible: d.evaluationStatus !== EditNetworkForm.UnTouched errorMessageCmp.visible: d.evaluationStatusMainRpc !== EditNetworkForm.UnTouched
errorMessageCmp.text: d.getUrlStatusText(d.evaluationStatus, text) errorMessageCmp.text: d.getUrlStatusText(d.evaluationStatusMainRpc, text)
errorMessageCmp.color: d.evaluationStatus === EditNetworkForm.Pending ? errorMessageCmp.color: {
Theme.palette.baseColor1: switch(d.evaluationStatusMainRpc) {
d.evaluationStatus === EditNetworkForm.Verified ? case EditNetworkForm.Pending:
Theme.palette.successColor1 : Theme.palette.dangerColor1 return Theme.palette.baseColor1
case EditNetworkForm.SameAsOther:
return Theme.palette.warningColor1
case EditNetworkForm.Verified:
return Theme.palette.successColor1
default: return Theme.palette.dangerColor1
}
}
} }
} }
@ -166,27 +181,36 @@ ColumnLayout {
if (!network) { if (!network) {
return "" return ""
} }
if (network.originalFallbackURL == network.fallbackURL) { if (network.originalFallbackURL === network.fallbackURL) {
return network.fallbackURL.replace(/(.*\/).*/, '$1') return network.fallbackURL.replace(/(.*\/).*/, '$1')
} }
return network.fallbackURL return network.fallbackURL
} }
onTextChanged: { onTextChanged: {
if(!!text && text !== network.fallbackURL.replace(/(.*\/).*/, '$1')) { if(!!text &&
(network.originalFallbackURL === network.fallbackURL && text !== network.fallbackURL.replace(/(.*\/).*/, '$1')) ||
(network.originalFallbackURL !== network.fallbackURL && text !== network.fallbackURL)) {
d.evaluationStatusFallBackRpc = EditNetworkForm.Pending d.evaluationStatusFallBackRpc = EditNetworkForm.Pending
Qt.callLater(d.evaluateRpcEndPoint, text); Qt.callLater(d.evaluateRpcEndPoint, text, false);
} }
} }
errorMessageCmp.horizontalAlignment: d.evaluationStatusFallBackRpc === EditNetworkForm.Pending || errorMessageCmp.horizontalAlignment: d.evaluationStatusFallBackRpc === EditNetworkForm.Pending ||
d.evaluationStatusFallBackRpc === EditNetworkForm.Verified ? d.evaluationStatusFallBackRpc === EditNetworkForm.Verified ||
d.evaluationStatusFallBackRpc === EditNetworkForm.SameAsOther ?
Text.AlignLeft: Text.AlignRight Text.AlignLeft: Text.AlignRight
errorMessageCmp.visible: d.evaluationStatusFallBackRpc !== EditNetworkForm.UnTouched errorMessageCmp.visible: d.evaluationStatusFallBackRpc !== EditNetworkForm.UnTouched
errorMessageCmp.text: d.getUrlStatusText(d.evaluationStatusFallBackRpc, text) errorMessageCmp.text: d.getUrlStatusText(d.evaluationStatusFallBackRpc, text)
errorMessageCmp.color: d.evaluationStatusFallBackRpc === EditNetworkForm.Pending ? errorMessageCmp.color: {
Theme.palette.baseColor1: switch(d.evaluationStatusFallBackRpc) {
d.evaluationStatusFallBackRpc === EditNetworkForm.Verified ? case EditNetworkForm.Pending:
Theme.palette.successColor1 : Theme.palette.dangerColor1 return Theme.palette.baseColor1
case EditNetworkForm.SameAsOther:
return Theme.palette.warningColor1
case EditNetworkForm.Verified:
return Theme.palette.successColor1
default: return Theme.palette.dangerColor1
}
}
} }
StatusInput { StatusInput {
@ -212,12 +236,12 @@ ColumnLayout {
StatusButton { StatusButton {
text: qsTr("Revert to default") text: qsTr("Revert to default")
normalColor: "transparent" normalColor: "transparent"
enabled: d.evaluationStatus !== EditNetworkForm.UnTouched || d.evaluationStatusFallBackRpc !== EditNetworkForm.UnTouched enabled: d.evaluationStatusMainRpc !== EditNetworkForm.UnTouched || d.evaluationStatusFallBackRpc !== EditNetworkForm.UnTouched
onClicked: d.revertValues() onClicked: d.revertValues()
} }
StatusButton { StatusButton {
text: qsTr("Save Changes") text: qsTr("Save Changes")
enabled: (d.evaluationStatus === EditNetworkForm.Verified || d.evaluationStatusFallBackRpc === EditNetworkForm.Verified) && warningCheckbox.checked enabled: (d.evaluationStatusMainRpc === EditNetworkForm.Verified || d.evaluationStatusFallBackRpc === EditNetworkForm.Verified || d.evaluationStatusMainRpc === EditNetworkForm.SameAsOther || d.evaluationStatusFallBackRpc === EditNetworkForm.SameAsOther) && warningCheckbox.checked
onClicked: root.updateNetworkValues(network.chainId, mainRpcInput.text, failoverRpcUrlInput.text) onClicked: root.updateNetworkValues(network.chainId, mainRpcInput.text, failoverRpcUrlInput.text)
} }
} }

View File

@ -13,7 +13,7 @@ ColumnLayout {
property var networksModule property var networksModule
property var combinedNetwork property var combinedNetwork
signal evaluateRpcEndPoint(string url) signal evaluateRpcEndPoint(string url, bool isMainUrl)
signal updateNetworkValues(int chainId, string newMainRpcInput, string newFailoverRpcUrl) signal updateNetworkValues(int chainId, string newMainRpcInput, string newFailoverRpcUrl)
StatusTabBar { StatusTabBar {
@ -39,7 +39,7 @@ ColumnLayout {
EditNetworkForm { EditNetworkForm {
network: !!root.combinedNetwork ? root.combinedNetwork.prod: null network: !!root.combinedNetwork ? root.combinedNetwork.prod: null
networksModule: root.networksModule networksModule: root.networksModule
onEvaluateRpcEndPoint: root.evaluateRpcEndPoint(url) onEvaluateRpcEndPoint: root.evaluateRpcEndPoint(url, isMainUrl)
onUpdateNetworkValues: root.updateNetworkValues(chainId, newMainRpcInput, newFailoverRpcUrl) onUpdateNetworkValues: root.updateNetworkValues(chainId, newMainRpcInput, newFailoverRpcUrl)
} }
} }
@ -49,7 +49,7 @@ ColumnLayout {
EditNetworkForm { EditNetworkForm {
network: !!root.combinedNetwork ? root.combinedNetwork.test: null network: !!root.combinedNetwork ? root.combinedNetwork.test: null
networksModule: root.networksModule networksModule: root.networksModule
onEvaluateRpcEndPoint: root.evaluateRpcEndPoint(url) onEvaluateRpcEndPoint: root.evaluateRpcEndPoint(url, isMainUrl)
onUpdateNetworkValues: root.updateNetworkValues(chainId, newMainRpcInput, newFailoverRpcUrl) onUpdateNetworkValues: root.updateNetworkValues(chainId, newMainRpcInput, newFailoverRpcUrl)
} }
} }