feat(dapp) handle pairing errors or timeout if no response
Found out while testing that in some corner cases there will be no response of error in case of pairing. This is handled now by showing a generic error message. The implementation is using a timer to handle this case. Extend the logic to report errors in the pairing process. Closes #14676
This commit is contained in:
parent
1e256c8bf1
commit
136194c112
|
@ -88,6 +88,7 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
StatusBaseText { text: "Custom Accounts" }
|
||||
StatusTextArea {
|
||||
text: settings.customAccounts
|
||||
onTextChanged: {
|
||||
|
@ -99,7 +100,8 @@ Item {
|
|||
})
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: !!text ? 400 : -1
|
||||
Layout.maximumHeight: 300
|
||||
clip: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
|
|
@ -185,9 +185,9 @@ DappsComboBox {
|
|||
Connections {
|
||||
target: root.wcService
|
||||
|
||||
function onPairingUriValidated(validationState) {
|
||||
function onPairingValidated(validationState) {
|
||||
if (pairWCLoader.item) {
|
||||
pairWCLoader.item.pairingUriValidated(validationState)
|
||||
pairWCLoader.item.pairingValidated(validationState)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,15 @@ import utils 1.0
|
|||
|
||||
import "types"
|
||||
|
||||
// The WC SDK has an async (function call then signal response)
|
||||
// A complete pairing flow to connect a dApp:
|
||||
// - user provides pairing url -> root.validatePairingUri -> signal pairingValidated
|
||||
// - user requests pair -> root.pair(uri) -> pairResponse(ok)
|
||||
// -> if pairResponse ok -> onSessionProposal -> sdk.buildApprovedNamespaces
|
||||
// -> onBuildApprovedNamespace -> signal connectDApp
|
||||
// - user requests root.approvePairSession/root.rejectPairSession
|
||||
// -> if approvePairSession -> sdk.buildApprovedNamespaces
|
||||
// -> onBuildApprovedNamespace -> sdk.approveSession -> onApproveSessionResult
|
||||
QObject {
|
||||
id: root
|
||||
|
||||
|
@ -47,38 +56,39 @@ QObject {
|
|||
|
||||
function validatePairingUri(uri) {
|
||||
if(Helpers.containsOnlyEmoji(uri)) {
|
||||
root.pairingUriValidated(Pairing.uriErrors.tooCool)
|
||||
root.pairingValidated(Pairing.errors.tooCool)
|
||||
return
|
||||
} else if(!Helpers.validURI(uri)) {
|
||||
root.pairingUriValidated(Pairing.uriErrors.invalidUri)
|
||||
root.pairingValidated(Pairing.errors.invalidUri)
|
||||
return
|
||||
}
|
||||
|
||||
let info = Helpers.extractInfoFromPairUri(uri)
|
||||
wcSDK.getActiveSessions((sessions) => {
|
||||
// Check if the URI is already paired
|
||||
var validationState = Pairing.uriErrors.ok
|
||||
var validationState = Pairing.errors.ok
|
||||
for (let key in sessions) {
|
||||
if (sessions[key].pairingTopic == info.topic) {
|
||||
validationState = Pairing.uriErrors.alreadyUsed
|
||||
validationState = Pairing.errors.alreadyUsed
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Check if expired
|
||||
if (validationState == Pairing.uriErrors.ok) {
|
||||
if (validationState == Pairing.errors.ok) {
|
||||
const now = (new Date().getTime())/1000
|
||||
if (info.expiry < now) {
|
||||
validationState = Pairing.uriErrors.expired
|
||||
validationState = Pairing.errors.expired
|
||||
}
|
||||
}
|
||||
|
||||
root.pairingUriValidated(validationState)
|
||||
root.pairingValidated(validationState)
|
||||
});
|
||||
}
|
||||
|
||||
function pair(uri) {
|
||||
d.acceptedSessionProposal = null
|
||||
timeoutTimer.start()
|
||||
wcSDK.pair(uri)
|
||||
}
|
||||
|
||||
|
@ -116,11 +126,19 @@ QObject {
|
|||
signal approveSessionResult(var session, var error)
|
||||
signal sessionRequest(SessionRequestResolved request)
|
||||
signal displayToastMessage(string message, bool error)
|
||||
signal pairingUriValidated(int validationState)
|
||||
// Emitted as a response to WalletConnectService.validatePairingUri or other WalletConnectService.pair
|
||||
// and WalletConnectService.approvePair errors
|
||||
signal pairingValidated(int validationState)
|
||||
|
||||
readonly property Connections sdkConnections: Connections {
|
||||
target: wcSDK
|
||||
|
||||
function onPairResponse(ok) {
|
||||
if (!ok) {
|
||||
d.reportPairErrorState(Pairing.errors.unknownError)
|
||||
} // else waiting for onSessionProposal
|
||||
}
|
||||
|
||||
function onSessionProposal(sessionProposal) {
|
||||
d.currentSessionProposal = sessionProposal
|
||||
|
||||
|
@ -133,9 +151,9 @@ QObject {
|
|||
if(error) {
|
||||
// Check that it contains Non conforming namespaces"
|
||||
if (error.includes("Non conforming namespaces")) {
|
||||
root.pairingUriValidated(Pairing.uriErrors.unsupportedNetwork)
|
||||
d.reportPairErrorState(Pairing.errors.unsupportedNetwork)
|
||||
} else {
|
||||
root.pairingUriValidated(Pairing.uriErrors.unknownError)
|
||||
d.reportPairErrorState(Pairing.errors.unknownError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -151,8 +169,7 @@ QObject {
|
|||
|
||||
function onApproveSessionResult(session, err) {
|
||||
if (err) {
|
||||
// TODO #14676: handle the error
|
||||
console.error("Failed to approve session", err)
|
||||
d.reportPairErrorState(Pairing.errors.unknownError)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -176,6 +193,7 @@ QObject {
|
|||
const app_url = d.currentSessionProposal ? d.currentSessionProposal.params.proposer.metadata.url : "-"
|
||||
const app_domain = StringUtils.extractDomainFromLink(app_url)
|
||||
if(err) {
|
||||
d.reportPairErrorState(Pairing.errors.unknownError)
|
||||
root.displayToastMessage(qsTr("Failed to reject connection request for %1").arg(app_domain), true)
|
||||
} else {
|
||||
root.displayToastMessage(qsTr("Connection request for %1 was rejected").arg(app_domain), false)
|
||||
|
@ -202,21 +220,9 @@ QObject {
|
|||
property var currentSessionProposal: null
|
||||
property var acceptedSessionProposal: null
|
||||
|
||||
// TODO #14676: use it to check if already paired
|
||||
function getPairingTopicFromPairingUrl(url)
|
||||
{
|
||||
if (!url.startsWith("wc:"))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
const atIndex = url.indexOf("@");
|
||||
if (atIndex < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return url.slice(3, atIndex);
|
||||
function reportPairErrorState(state) {
|
||||
timeoutTimer.stop()
|
||||
root.pairingValidated(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,6 +239,7 @@ QObject {
|
|||
networksModel: root.flatNetworks
|
||||
|
||||
onSessionRequest: (request) => {
|
||||
timeoutTimer.stop()
|
||||
root.sessionRequest(request)
|
||||
}
|
||||
onDisplayToastMessage: (message, error) => {
|
||||
|
@ -246,4 +253,17 @@ QObject {
|
|||
sdk: root.wcSDK
|
||||
store: root.store
|
||||
}
|
||||
|
||||
// Timeout for the corner case where the URL was already dismissed and the SDK doesn't respond with an error nor advances with the proposal
|
||||
Timer {
|
||||
id: timeoutTimer
|
||||
|
||||
interval: 10000 // (10 seconds)
|
||||
running: false
|
||||
repeat: false
|
||||
|
||||
onTriggered: {
|
||||
d.reportPairErrorState(Pairing.errors.unknownError)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ pragma Singleton
|
|||
import QtQml 2.15
|
||||
|
||||
QtObject {
|
||||
readonly property QtObject uriErrors: QtObject {
|
||||
readonly property QtObject errors: QtObject {
|
||||
readonly property int notChecked: 0
|
||||
readonly property int ok: 1
|
||||
readonly property int tooCool: 2
|
||||
|
|
|
@ -27,9 +27,9 @@ StatusDialog {
|
|||
|
||||
property bool isPairing: false
|
||||
|
||||
function pairingUriValidated(validationState) {
|
||||
function pairingValidated(validationState) {
|
||||
uriInput.errorState = validationState
|
||||
if (validationState === Pairing.uriErrors.ok) {
|
||||
if (validationState === Pairing.errors.ok) {
|
||||
d.doPair()
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ StatusDialog {
|
|||
WCUriInput {
|
||||
id: uriInput
|
||||
|
||||
pending: uriInput.errorState === Pairing.uriErrors.notChecked
|
||||
pending: uriInput.errorState === Pairing.errors.notChecked
|
||||
|
||||
onTextChanged: {
|
||||
root.isPairing = false
|
||||
|
@ -91,7 +91,7 @@ StatusDialog {
|
|||
enabled: uriInput.valid
|
||||
&& !root.isPairing
|
||||
&& uriInput.text.length > 0
|
||||
&& uriInput.errorState === Pairing.uriErrors.ok
|
||||
&& uriInput.errorState === Pairing.errors.ok
|
||||
|
||||
onClicked: {
|
||||
d.doPair()
|
||||
|
|
|
@ -17,7 +17,7 @@ ColumnLayout {
|
|||
readonly property bool valid: input.valid && input.text.length > 0
|
||||
readonly property alias text: input.text
|
||||
property alias pending: input.pending
|
||||
property int errorState: Pairing.uriErrors.notChecked
|
||||
property int errorState: Pairing.errors.notChecked
|
||||
|
||||
StatusBaseInput {
|
||||
id: input
|
||||
|
@ -37,22 +37,21 @@ ColumnLayout {
|
|||
return true
|
||||
}
|
||||
|
||||
if(root.errorState === Pairing.uriErrors.tooCool) {
|
||||
if(root.errorState === Pairing.errors.tooCool) {
|
||||
errorText.text = qsTr("WalletConnect URI too cool")
|
||||
} else if(root.errorState === Pairing.uriErrors.invalidUri) {
|
||||
} else if(root.errorState === Pairing.errors.invalidUri) {
|
||||
errorText.text = qsTr("WalletConnect URI invalid")
|
||||
} else if(root.errorState === Pairing.uriErrors.alreadyUsed) {
|
||||
} else if(root.errorState === Pairing.errors.alreadyUsed) {
|
||||
errorText.text = qsTr("WalletConnect URI already used")
|
||||
} else if(root.errorState === Pairing.uriErrors.expired) {
|
||||
} else if(root.errorState === Pairing.errors.expired) {
|
||||
errorText.text = qsTr("WalletConnect URI has expired")
|
||||
}
|
||||
if (errorText.text.length > 0) {
|
||||
return false
|
||||
} else if(root.errorState === Pairing.uriErrors.unsupportedNetwork) {
|
||||
} else if(root.errorState === Pairing.errors.unsupportedNetwork) {
|
||||
errorText.text = qsTr("dApp is requesting to connect on an unsupported network")
|
||||
return false
|
||||
} else if(root.errorState === Pairing.uriErrors.unknownError) {
|
||||
errorText.text = qsTr("Unexpected error occurred, please try again")
|
||||
} else if(root.errorState === Pairing.errors.unknownError) {
|
||||
errorText.text = qsTr("Unexpected error occurred. Try again.")
|
||||
}
|
||||
|
||||
if (errorText.text.length > 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue