diff --git a/src/app/modules/shared_modules/wallet_connect/controller.nim b/src/app/modules/shared_modules/wallet_connect/controller.nim index d81e509c49..d21522f55e 100644 --- a/src/app/modules/shared_modules/wallet_connect/controller.nim +++ b/src/app/modules/shared_modules/wallet_connect/controller.nim @@ -1,5 +1,5 @@ import NimQml -import chronicles +import chronicles, times, json import app_service/service/wallet_connect/service as wallet_connect_service import app_service/service/wallet_account/service as wallet_account_service @@ -47,6 +47,20 @@ QtObject: self.dappsListReceived(res) return true + proc activeSessionsReceived(self: Controller, activeSessionsJson: string) {.signal.} + + # Emits signal activeSessionsReceived with the list of active sessions + # TODO: make it async + proc getActiveSessions(self: Controller): bool {.slot.} = + let validAtTimestamp = now().toTime().toUnix() + let res = self.service.getActiveSessions(validAtTimestamp) + if res.isNil: + return false + else: + let resultStr = $res + self.activeSessionsReceived(resultStr) + return true + proc userAuthenticationResult*(self: Controller, topic: string, id: string, error: bool, password: string, pin: string, payload: string) {.signal.} # Beware, it will fail if an authentication is already in progress diff --git a/src/app_service/service/wallet_connect/service.nim b/src/app_service/service/wallet_connect/service.nim index 9b04c10529..15c8a0c404 100644 --- a/src/app_service/service/wallet_connect/service.nim +++ b/src/app_service/service/wallet_connect/service.nim @@ -114,6 +114,11 @@ QtObject: let testChains = self.settingsService.areTestNetworksEnabled() # TODO #14588: call it async return status_go.getDapps(validAtEpoch, testChains) + + proc getActiveSessions*(self: Service, validAtTimestamp: int64): JsonNode = + # TODO #14588: call it async + return status_go.getActiveSessions(validAtTimestamp) + # Will fail if another authentication is in progress proc authenticateUser*(self: Service, keyUid: string, callback: AuthenticationResponseFn): bool = diff --git a/src/backend/wallet_connect.nim b/src/backend/wallet_connect.nim index d3863da3ba..d3abba0844 100644 --- a/src/backend/wallet_connect.nim +++ b/src/backend/wallet_connect.nim @@ -18,7 +18,7 @@ rpc(disconnectWalletConnectSession, "wallet"): topic: string rpc(getWalletConnectActiveSessions, "wallet"): - validAtTimestamp: int + validAtTimestamp: int64 rpc(hashMessageEIP191, "wallet"): message: string @@ -50,13 +50,14 @@ proc disconnectSession*(topic: string): bool = return false # returns nil if error -proc getActiveSessions*(validAtTimestamp: int): JsonNode = +proc getActiveSessions*(validAtTimestamp: int64): JsonNode = try: let rpcRes = getWalletConnectActiveSessions(validAtTimestamp) + if(not isSuccessResponse(rpcRes)): return nil - let jsonResultStr = rpcRes.result.getStr() + let jsonResultStr = $rpcRes.result if jsonResultStr == "null" or jsonResultStr == "": return newJArray() diff --git a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml index 5638c7e2ae..b9b42c2a76 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml +++ b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml @@ -236,16 +236,7 @@ QObject { } function onSessionDelete(topic, err) { - store.deactivateWalletConnectSession(topic) - dappsProvider.updateDapps() - - const app_url = d.currentSessionProposal ? d.currentSessionProposal.params.proposer.metadata.url : "-" - const app_domain = StringUtils.extractDomainFromLink(app_url) - if(err) { - root.displayToastMessage(qsTr("Failed to disconnect from %1").arg(app_domain), true) - } else { - root.displayToastMessage(qsTr("Disconnected from %1").arg(app_domain), false) - } + d.disconnectSessionRequested(topic, err) } } @@ -267,6 +258,54 @@ QObject { timeoutTimer.stop() root.pairingValidated(state) } + + function disconnectSessionRequested(topic, err) { + // Get all sessions and filter the active ones for known accounts + // Act on the first matching session with the same topic + const activeSessionsCallback = (allSessions, success) => { + store.activeSessionsReceived.disconnect(activeSessionsCallback) + + if (!success) { + // TODO #14754: implement custom dApp notification + d.notifyDappDisconnect("-", true) + return + } + + // Convert to original format + const webSdkSessions = allSessions.map((session) => { + return JSON.parse(session.sessionJson) + }) + + const sessions = DAppsHelpers.filterActiveSessionsForKnownAccounts(webSdkSessions, root.validAccounts) + + for (const sessionID in sessions) { + const session = sessions[sessionID] + if (session.topic === topic) { + store.deactivateWalletConnectSession(topic) + dappsProvider.updateDapps() + + const dappUrl = session.peer.metadata.url ?? "-" + d.notifyDappDisconnect(dappUrl, err) + break + } + } + } + + store.activeSessionsReceived.connect(activeSessionsCallback) + if (!store.getActiveSessions()) { + store.activeSessionsReceived.disconnect(activeSessionsCallback) + // TODO #14754: implement custom dApp notification + } + } + + function notifyDappDisconnect(dappUrl, err) { + const appDomain = StringUtils.extractDomainFromLink(dappUrl) + if(err) { + root.displayToastMessage(qsTr("Failed to disconnect from %1").arg(appDomain), true) + } else { + root.displayToastMessage(qsTr("Disconnected from %1").arg(appDomain), false) + } + } } Component.onCompleted: { diff --git a/ui/imports/shared/stores/DAppsStore.qml b/ui/imports/shared/stores/DAppsStore.qml index 5a6825af52..af316f2511 100644 --- a/ui/imports/shared/stores/DAppsStore.qml +++ b/ui/imports/shared/stores/DAppsStore.qml @@ -8,6 +8,7 @@ QObject { required property var controller /// \c dappsJson serialized from status-go.wallet.GetDapps signal dappsListReceived(string dappsJson) + signal activeSessionsReceived(var activeSessionsJsonObj, bool success) signal userAuthenticated(string topic, string id, string password, string pin, string payload) signal userAuthenticationFailed(string topic, string id) @@ -106,6 +107,12 @@ QObject { function getDapps() { return controller.getDapps() } + + /// \c getActiveSessions triggers an async response to \c activeSessionsReceived + /// \returns true if the request was sent successfully + function getActiveSessions() { + return controller.getActiveSessions() + } function hexToDec(hex) { return controller.hexToDecBigString(hex) @@ -124,6 +131,17 @@ QObject { root.dappsListReceived(dappsJson) } + function onActiveSessionsReceived(activeSessionsJson) { + try { + const jsonObj = JSON.parse(activeSessionsJson) + root.activeSessionsReceived(jsonObj, true) + } catch (e) { + console.error("Failed to parse activeSessionsJson", e) + root.activeSessionsReceived({}, false) + return + } + } + function onUserAuthenticationResult(topic, id, success, password, pin, payload) { if (success) { root.userAuthenticated(topic, id, password, pin, payload)