diff --git a/src/app/boot/app_controller.nim b/src/app/boot/app_controller.nim
index f9473a80dc..a619eaf371 100644
--- a/src/app/boot/app_controller.nim
+++ b/src/app/boot/app_controller.nim
@@ -192,7 +192,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
statusFoundation.events, statusFoundation.threadpool, result.settingsService, result.accountsService,
result.tokenService, result.networkService, result.currencyService
)
- result.walletConnectService = wallet_connect_service.newService(statusFoundation.events, statusFoundation.threadpool)
+ result.walletConnectService = wallet_connect_service.newService(statusFoundation.events, statusFoundation.threadpool, result.settingsService)
result.messageService = message_service.newService(
statusFoundation.events,
statusFoundation.threadpool,
diff --git a/src/app/modules/main/wallet_section/module.nim b/src/app/modules/main/wallet_section/module.nim
index ee3ffa336b..6a50dfa05a 100644
--- a/src/app/modules/main/wallet_section/module.nim
+++ b/src/app/modules/main/wallet_section/module.nim
@@ -18,12 +18,12 @@ import ./activity/controller as activityc
import ./activity/details_controller as activity_detailsc
import app/modules/shared_modules/collectible_details/controller as collectible_detailsc
+import app/modules/shared_modules/wallet_connect/controller as wc_controller
import app/global/global_singleton
import app/core/eventemitter
import app/modules/shared_modules/add_account/module as add_account_module
import app/modules/shared_modules/keypair_import/module as keypair_import_module
-import app/modules/shared_modules/wallet_connect/module as wc_module
import app_service/service/keycard/service as keycard_service
import app_service/service/token/service as token_service
import app_service/service/collectible/service as collectible_service
@@ -61,7 +61,6 @@ type
# shared modules
addAccountModule: add_account_module.AccessInterface
keypairImportModule: keypair_import_module.AccessInterface
- walletConnectModule: wc_module.AccessInterface
# modules
accountsModule: accounts_module.AccessInterface
allTokensModule: all_tokens_module.AccessInterface
@@ -80,6 +79,7 @@ type
savedAddressService: saved_address_service.Service
devicesService: devices_service.Service
walletConnectService: wc_service.Service
+ walletConnectController: wc_controller.Controller
activityController: activityc.Controller
collectibleDetailsController: collectible_detailsc.Controller
@@ -167,10 +167,10 @@ proc newModule*(
result.collectibleDetailsController = collectible_detailsc.newController(int32(backend_collectibles.CollectiblesRequestID.WalletAccount), networkService, events)
result.filter = initFilter(result.controller)
- result.walletConnectService = wc_service.newService(result.events, result.threadpool)
- result.walletConnectModule = wc_module.newModule(result, result.events, result.walletAccountService, result.walletConnectService)
+ result.walletConnectService = wc_service.newService(result.events, result.threadpool, settingsService)
+ result.walletConnectController = wc_controller.newController(result.walletConnectService)
- result.view = newView(result, result.activityController, result.tmpActivityControllers, result.activityDetailsController, result.collectibleDetailsController, result.walletConnectModule)
+ result.view = newView(result, result.activityController, result.tmpActivityControllers, result.activityDetailsController, result.collectibleDetailsController, result.walletConnectController)
method delete*(self: Module) =
self.accountsModule.delete
diff --git a/src/app/modules/main/wallet_section/view.nim b/src/app/modules/main/wallet_section/view.nim
index b33bf01633..d0eb0359d2 100644
--- a/src/app/modules/main/wallet_section/view.nim
+++ b/src/app/modules/main/wallet_section/view.nim
@@ -5,7 +5,7 @@ import ./activity/details_controller as activity_detailsc
import app/modules/shared_modules/collectible_details/controller as collectible_detailsc
import ./io_interface
import app/modules/shared_models/currency_amount
-import app/modules/shared_modules/wallet_connect/io_interface as wc_module
+import app/modules/shared_modules/wallet_connect/controller as wc_controller
type
ActivityControllerArray* = array[2, activityc.Controller]
@@ -25,7 +25,7 @@ QtObject:
collectibleDetailsController: collectible_detailsc.Controller
isNonArchivalNode: bool
keypairOperabilityForObservedAccount: string
- wcModule: wc_module.AccessInterface
+ wcController: QVariant
walletReady: bool
addressFilters: string
currentCurrency: string
@@ -34,6 +34,8 @@ QtObject:
self.QObject.setup
proc delete*(self: View) =
+ self.wcController.delete
+
self.QObject.delete
proc newView*(delegate: io_interface.AccessInterface,
@@ -41,14 +43,14 @@ QtObject:
tmpActivityControllers: ActivityControllerArray,
activityDetailsController: activity_detailsc.Controller,
collectibleDetailsController: collectible_detailsc.Controller,
- wcModule: wc_module.AccessInterface): View =
+ wcController: wc_controller.Controller): View =
new(result, delete)
result.delegate = delegate
result.activityController = activityController
result.tmpActivityControllers = tmpActivityControllers
result.activityDetailsController = activityDetailsController
result.collectibleDetailsController = collectibleDetailsController
- result.wcModule = wcModule
+ result.wcController = newQVariant(wcController)
result.setup()
@@ -236,13 +238,13 @@ QtObject:
proc emitDestroyKeypairImportPopup*(self: View) =
self.destroyKeypairImportPopup()
- proc getWalletConnectModule(self: View): QVariant {.slot.} =
- if self.wcModule == nil:
+ proc getWalletConnectController(self: View): QVariant {.slot.} =
+ if self.wcController == nil:
return newQVariant()
- return self.wcModule.getModuleAsVariant()
+ return self.wcController
- QtProperty[QVariant] walletConnectModule:
- read = getWalletConnectModule
+ QtProperty[QVariant] walletConnectController:
+ read = getWalletConnectController
proc walletReadyChanged*(self: View) {.signal.}
diff --git a/src/app/modules/shared_modules/wallet_connect/controller.nim b/src/app/modules/shared_modules/wallet_connect/controller.nim
index 2124e6b51f..61112805fb 100644
--- a/src/app/modules/shared_modules/wallet_connect/controller.nim
+++ b/src/app/modules/shared_modules/wallet_connect/controller.nim
@@ -1,33 +1,36 @@
+import NimQml
import chronicles
-import io_interface
-import app/core/eventemitter
-import app_service/service/wallet_account/service as wallet_account_service
import app_service/service/wallet_connect/service as wallet_connect_service
logScope:
topics = "wallet-connect-controller"
-type
- Controller* = ref object of RootObj
- delegate: io_interface.AccessInterface
- events: EventEmitter
- walletAccountService: wallet_account_service.Service
- walletConnectService: wallet_connect_service.Service
+QtObject:
+ type
+ Controller* = ref object of QObject
+ service: wallet_connect_service.Service
-proc newController*(delegate: io_interface.AccessInterface,
- events: EventEmitter,
- walletAccountService: wallet_account_service.Service,
- walletConnectService: wallet_connect_service.Service): Controller =
- result = Controller()
- result.delegate = delegate
- result.events = events
- result.walletAccountService = walletAccountService
- result.walletConnectService = walletConnectService
+ proc delete*(self: Controller) =
+ self.QObject.delete
-proc init*(self: Controller) =
- discard
+ proc newController*(service: wallet_connect_service.Service): Controller =
+ new(result, delete)
-proc addWalletConnectSession*(self: Controller, session_json: string): bool =
- echo "@ddd Controller.addWalletConnectSession", session_json.len
- return self.walletConnectService.addSession(session_json)
\ No newline at end of file
+ result.service = service
+
+ result.QObject.setup
+
+ proc addWalletConnectSession*(self: Controller, session_json: string): bool {.slot.} =
+ return self.service.addSession(session_json)
+
+ proc dappsListReceived*(self: Controller, dappsJson: string) {.signal.}
+
+ # Emits signal dappsListReceived with the list of dApps
+ proc getDapps*(self: Controller): bool {.slot.} =
+ let res = self.service.getDapps()
+ if res == "":
+ return false
+ else:
+ self.dappsListReceived(res)
+ return true
diff --git a/src/app/modules/shared_modules/wallet_connect/io_interface.nim b/src/app/modules/shared_modules/wallet_connect/io_interface.nim
deleted file mode 100644
index 45ebc5126b..0000000000
--- a/src/app/modules/shared_modules/wallet_connect/io_interface.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-import NimQml
-
-type
- AccessInterface* {.pure inheritable.} = ref object of RootObj
-
-method delete*(self: AccessInterface) {.base.} =
- raise newException(ValueError, "No implementation available")
-
-method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} =
- raise newException(ValueError, "No implementation available")
-
-method addWalletConnectSession*(self: AccessInterface, session_json: string): bool {.base.} =
- raise newException(ValueError, "No implementation available")
-
-type
- DelegateInterface* = concept c
diff --git a/src/app/modules/shared_modules/wallet_connect/module.nim b/src/app/modules/shared_modules/wallet_connect/module.nim
deleted file mode 100644
index d17f095884..0000000000
--- a/src/app/modules/shared_modules/wallet_connect/module.nim
+++ /dev/null
@@ -1,51 +0,0 @@
-import NimQml, chronicles
-
-import io_interface
-import view, controller
-import app/core/eventemitter
-
-import app_service/service/wallet_account/service as wallet_account_service
-import app_service/service/wallet_connect/service as wallet_connect_service
-import app_service/service/keychain/service as keychain_service
-
-export io_interface
-
-logScope:
- topics = "wallet-connect-module"
-
-type
- Module*[T: io_interface.DelegateInterface] = ref object of io_interface.AccessInterface
- delegate: T
- view: View
- viewVariant: QVariant
- controller: Controller
-
-proc newModule*[T](delegate: T,
- events: EventEmitter,
- walletAccountService: wallet_account_service.Service,
- walletConnectService: wallet_connect_service.Service):
- Module[T] =
- result = Module[T]()
- result.delegate = delegate
- result.view = view.newView(result)
- result.viewVariant = newQVariant(result.view)
- result.controller = controller.newController(result, events, walletAccountService, walletConnectService)
-
-proc addWalletConnectSession*[T](self: Module[T], session_json: string): bool =
- echo "@dd Module.addWalletConnectSession: ", session_json
- return self.controller.addWalletConnectSession(session_json)
-
-{.push warning[Deprecated]: off.}
-
-method delete*[T](self: Module[T]) =
- self.view.delete
- self.viewVariant.delete
-
-proc init[T](self: Module[T], fullConnect = true) =
- self.controller.init()
-
-method getModuleAsVariant*[T](self: Module[T]): QVariant =
- return self.viewVariant
-
-
-{.pop.}
diff --git a/src/app/modules/shared_modules/wallet_connect/view.nim b/src/app/modules/shared_modules/wallet_connect/view.nim
deleted file mode 100644
index ce1b8062e4..0000000000
--- a/src/app/modules/shared_modules/wallet_connect/view.nim
+++ /dev/null
@@ -1,23 +0,0 @@
-import NimQml
-import io_interface
-
-QtObject:
- type
- View* = ref object of QObject
- delegate: io_interface.AccessInterface
-
- proc delete*(self: View) =
- self.QObject.delete
-
- proc newView*(delegate: io_interface.AccessInterface): View =
- new(result, delete)
- result.QObject.setup
- result.delegate = delegate
-
- proc addWalletConnectSession*(self: View, session_json: string): bool {.slot.} =
- echo "@dd Vew.addWalletConnectSession: ", session_json, "; self.delegate.isNil: ", self.delegate.isNil
- try:
- return self.delegate.addWalletConnectSession(session_json)
- except Exception as e:
- echo "@dd Error in View.addWalletConnectSession: ", e.msg
- return false
\ No newline at end of file
diff --git a/src/app_service/service/wallet_connect/service.nim b/src/app_service/service/wallet_connect/service.nim
index 1dfda85155..ae15e27e5a 100644
--- a/src/app_service/service/wallet_connect/service.nim
+++ b/src/app_service/service/wallet_connect/service.nim
@@ -1,7 +1,9 @@
-import NimQml, chronicles
+import NimQml, chronicles, times
import backend/wallet_connect as status_go
+import app_service/service/settings/service as settings_service
+
import app/global/global_singleton
import app/core/eventemitter
@@ -17,6 +19,7 @@ QtObject:
type Service* = ref object of QObject
events: EventEmitter
threadpool: ThreadPool
+ settingsService: settings_service.Service
proc delete*(self: Service) =
self.QObject.delete
@@ -24,14 +27,23 @@ QtObject:
proc newService*(
events: EventEmitter,
threadpool: ThreadPool,
+ settingsService: settings_service.Service,
): Service =
new(result, delete)
result.QObject.setup
result.events = events
result.threadpool = threadpool
+ result.settingsService = settings_service
proc init*(self: Service) =
discard
proc addSession*(self: Service, session_json: string): bool =
- return status_go.addSession(session_json)
\ No newline at end of file
+ # TODO #14588: call it async
+ return status_go.addSession(session_json)
+
+ proc getDapps*(self: Service): string =
+ let validAtEpoch = now().toTime().toUnix()
+ let testChains = self.settingsService.areTestNetworksEnabled()
+ # TODO #14588: call it async
+ return status_go.getDapps(validAtEpoch, testChains)
\ No newline at end of file
diff --git a/src/backend/wallet_connect.nim b/src/backend/wallet_connect.nim
index 1d4a3ab4c5..8a60094cd1 100644
--- a/src/backend/wallet_connect.nim
+++ b/src/backend/wallet_connect.nim
@@ -1,5 +1,5 @@
import options, logging
-import json
+import json, json_serialization
import core, response_type
from gen import rpc
@@ -18,3 +18,18 @@ proc addSession*(sessionJson: string): bool =
except Exception as e:
warn "AddWalletConnectSession failed: ", "msg", e.msg
return false
+
+proc getDapps*(validAtEpoch: int64, testChains: bool): string =
+ try:
+ let params = %*[validAtEpoch, testChains]
+ let rpcResRaw = callPrivateRPCNoDecode("wallet_getWalletConnectDapps", params)
+ let rpcRes = Json.decode(rpcResRaw, RpcResponse[JsonNode])
+ if(not rpcRes.error.isNil):
+ return ""
+
+ # Expect nil golang array to be valid empty array
+ let jsonArray = $rpcRes.result
+ return if jsonArray != "null": jsonArray else: "[]"
+ except Exception as e:
+ warn "GetWalletConnectDapps failed: ", "msg", e.msg
+ return ""
diff --git a/storybook/pages/ConnectDAppModalPage.qml b/storybook/pages/ConnectDAppModalPage.qml
index b34381d460..a8d8be700b 100644
--- a/storybook/pages/ConnectDAppModalPage.qml
+++ b/storybook/pages/ConnectDAppModalPage.qml
@@ -32,7 +32,7 @@ Item {
"metadata": {
"description": "React App for WalletConnect",
"icons": [
- "https://avatars.githubusercontent.com/u/37784886"
+ "https://avatars.githubusercontent.com/u/37784886"
],
"name": "React App",
"url": "https://react-app.walletconnect.com",
diff --git a/storybook/pages/DAppsWorkflowPage.qml b/storybook/pages/DAppsWorkflowPage.qml
index 08c71a7df0..7533a06c04 100644
--- a/storybook/pages/DAppsWorkflowPage.qml
+++ b/storybook/pages/DAppsWorkflowPage.qml
@@ -98,15 +98,18 @@ Item {
}
}
-
- CheckBox {
-
- text: "Open Pair"
- checked: settings.openPair
- onCheckedChanged: {
- settings.openPair = checked
- if (checked) {
- d.startPairing()
+ ComboBox {
+ model: [{testCase: d.noTestCase, name: "No Test Case"},
+ {testCase: d.openDappsTestCase, name: "Open dApps"},
+ {testCase: d.openPairTestCase, name: "Open Pair"}
+ ]
+ textRole: "name"
+ valueRole: "testCase"
+ currentIndex: settings.testCase
+ onCurrentValueChanged: {
+ settings.testCase = currentValue
+ if (currentValue !== d.noTestCase) {
+ d.startTestCase()
}
}
@@ -114,8 +117,8 @@ Item {
target: dappsWorkflow
// If Open Pair workflow if selected in the side bar
- function onDAppsListReady() {
- if (!d.startPairingWorkflowActive)
+ function onDappsListReady() {
+ if (d.activeTestCase < d.openPairTestCase)
return
let items = InspectionUtils.findVisualsByTypeName(dappsWorkflow, "DAppsListPopup")
@@ -128,7 +131,7 @@ Item {
}
function onPairWCReady() {
- if (!d.startPairingWorkflowActive)
+ if (d.activeTestCase < d.openPairTestCase)
return
if (pairUriInput.text.length > 0) {
@@ -142,7 +145,7 @@ Item {
}
function clickDoneIfSDKReady() {
- if (!d.startPairingWorkflowActive) {
+ if (d.activeTestCase < d.openPairTestCase) {
return
}
@@ -150,7 +153,7 @@ Item {
if (modals.length === 1) {
let buttons = InspectionUtils.findVisualsByTypeName(modals[0].footer, "StatusButton")
if (buttons.length === 1 && walletConnectService.wcSDK.sdkReady) {
- d.startPairingWorkflowActive = false
+ d.activeTestCase = d.noTestCase
buttons[0].clicked()
return
}
@@ -173,8 +176,25 @@ Item {
}
store: DAppsStore {
+ signal dappsListReceived(string dappsJson)
+
function addWalletConnectSession(sessionJson) {
console.info("Persist Session", sessionJson)
+
+ let session = JSON.parse(sessionJson)
+
+ let firstIconUrl = session.peer.metadata.icons.length > 0 ? session.peer.metadata.icons[0] : ""
+ let persistedDapp = {
+ "name": session.peer.metadata.name,
+ "url": session.peer.metadata.url,
+ "iconUrl": firstIconUrl
+ }
+ d.persistedDapps.push(persistedDapp)
+ }
+
+ function getDapps() {
+ this.dappsListReceived(JSON.stringify(d.persistedDapps))
+ return true
}
}
@@ -191,26 +211,39 @@ Item {
QtObject {
id: d
- property bool startPairingWorkflowActive: false
+ property int activeTestCase: noTestCase
- function startPairing() {
- d.startPairingWorkflowActive = true
+ function startTestCase() {
+ d.activeTestCase = settings.testCase
if(root.visible) {
dappsWorkflow.clicked()
}
}
+
+ readonly property int noTestCase: 0
+ readonly property int openDappsTestCase: 1
+ readonly property int openPairTestCase: 2
+
+ property var persistedDapps: [
+ {"name":"Test dApp 1", "url":"https://dapp.test/1","iconUrl":"https://se-sdk-dapp.vercel.app/assets/eip155:1.png"},
+ {"name":"Test dApp 2", "url":"https://dapp.test/2","iconUrl":"https://react-app.walletconnect.com/assets/eip155-1.png"},
+ {"name":"Test dApp 3", "url":"https://dapp.test/3","iconUrl":"https://react-app.walletconnect.com/assets/eip155-1.png"},
+ {"name":"Test dApp 4 - very long name !!!!!!!!!!!!!!!!", "url":"https://dapp.test/4","iconUrl":"https://react-app.walletconnect.com/assets/eip155-1.png"},
+ {"name":"Test dApp 5 - very long url", "url":"https://dapp.test/very_long/url/unusual","iconUrl":"https://react-app.walletconnect.com/assets/eip155-1.png"},
+ {"name":"Test dApp 6", "url":"https://dapp.test/6","iconUrl":"https://react-app.walletconnect.com/assets/eip155-1.png"}
+ ]
}
onVisibleChanged: {
- if (visible && d.startPairingWorkflowActive) {
- d.startPairing()
+ if (visible && d.activeTestCase !== d.noTestCase) {
+ d.startTestCase()
}
}
Settings {
id: settings
- property bool openPair: false
+ property int testCase: d.noTestCase
property string pairUri: ""
property bool testNetworks: false
}
diff --git a/ui/StatusQ/src/assets.qrc b/ui/StatusQ/src/assets.qrc
index 73cf882d29..85067c1642 100644
--- a/ui/StatusQ/src/assets.qrc
+++ b/ui/StatusQ/src/assets.qrc
@@ -311,6 +311,7 @@
assets/img/icons/token-sale.svg
assets/img/icons/token.svg
assets/img/icons/destroy.svg
+ assets/img/icons/disconnect.svg
assets/img/icons/touch-id.svg
assets/img/icons/travel-and-places.svg
assets/img/icons/tributeToTalk.svg
diff --git a/ui/StatusQ/src/assets/img/icons/disconnect.svg b/ui/StatusQ/src/assets/img/icons/disconnect.svg
new file mode 100644
index 0000000000..b2e01f1ce8
--- /dev/null
+++ b/ui/StatusQ/src/assets/img/icons/disconnect.svg
@@ -0,0 +1,5 @@
+
diff --git a/ui/app/AppLayouts/Wallet/panels/DAppsWorkflow.qml b/ui/app/AppLayouts/Wallet/panels/DAppsWorkflow.qml
index 1692f20e29..d599444e62 100644
--- a/ui/app/AppLayouts/Wallet/panels/DAppsWorkflow.qml
+++ b/ui/app/AppLayouts/Wallet/panels/DAppsWorkflow.qml
@@ -14,7 +14,7 @@ ConnectedDappsButton {
required property WalletConnectService wcService
- signal dAppsListReady()
+ signal dappsListReady()
signal pairWCReady()
onClicked: {
@@ -52,18 +52,20 @@ ConnectedDappsButton {
onLoaded: {
item.open()
- root.dAppsListReady()
+ root.dappsListReady()
}
sourceComponent: DAppsListPopup {
visible: true
+ model: wcService.dappsModel
+
onPairWCDapp: {
pairWCLoader.active = true
this.close()
}
onOpened: {
- this.x = root.width - this.menuWidth - 2 * this.padding
+ this.x = root.width - this.contentWidth - 2 * this.padding
this.y = root.height + 4
}
onClosed: dappsListLoader.active = false
diff --git a/ui/app/AppLayouts/Wallet/services/dapps/DAppsListProvider.qml b/ui/app/AppLayouts/Wallet/services/dapps/DAppsListProvider.qml
index 6ed3b12dc1..5815e62ad9 100644
--- a/ui/app/AppLayouts/Wallet/services/dapps/DAppsListProvider.qml
+++ b/ui/app/AppLayouts/Wallet/services/dapps/DAppsListProvider.qml
@@ -1,79 +1,86 @@
import QtQuick 2.15
+import StatusQ.Core.Utils 0.1
+
+import shared.stores 1.0
+
import utils 1.0
-QtObject {
+QObject {
id: root
required property WalletConnectSDK sdk
+ required property DAppsStore store
- readonly property alias pairingsModel: d.pairingsModel
- readonly property alias sessionsModel: d.sessionsModel
+ readonly property alias dappsModel: d.dappsModel
- function updatePairings() {
- d.resetPairingsModel()
- }
- function updateSessions() {
- d.resetSessionsModel()
+ function updateDapps() {
+ d.updateDappsModel()
}
- readonly property QtObject _d: QtObject {
+ QObject {
id: d
- property ListModel pairingsModel: ListModel {
- id: pairings
- }
- property ListModel sessionsModel: ListModel {
- id: sessions
+ property ListModel dappsModel: ListModel {
+ id: dapps
}
- function resetPairingsModel(entryCallback)
+ property var dappsListReceivedFn: null
+ property var getActiveSessionsFn: null
+ function updateDappsModel()
{
- pairings.clear();
+ dappsListReceivedFn = (dappsJson) => {
+ dapps.clear();
- // We have to postpone `getPairings` call, cause otherwise:
- // - the last made pairing will always have `active` prop set to false
+ let dappsList = JSON.parse(dappsJson);
+ for (let i = 0; i < dappsList.length; i++) {
+ dapps.append(dappsList[i]);
+ }
+ }
+ root.store.dappsListReceived.connect(dappsListReceivedFn);
+
+ // triggers a potential fast response from store.dappsListReceived
+ if (!store.getDapps()) {
+ console.warn("Failed retrieving dapps from persistence")
+ root.store.dappsListReceived.disconnect(dappsListReceivedFn);
+ }
+
+ // TODO DEV: check if still holds true
+ // Reasons to postpone `getDapps` call:
+ // - the first recent made session will always have `active` prop set to false
// - expiration date won't be the correct one, but one used in session proposal
- // - the list of pairings will display succesfully made pairing as inactive
- Backpressure.debounce(this, 250, () => {
- sdk.getPairings((pairList) => {
- for (let i = 0; i < pairList.length; i++) {
- pairings.append(pairList[i]);
+ // - the list of dapps will display successfully made pairing as inactive
+ getActiveSessionsFn = () => {
+ sdk.getActiveSessions((sessions) => {
+ root.store.dappsListReceived.disconnect(dappsListReceivedFn);
- if (entryCallback) {
- entryCallback(pairList[i])
+ // TODO #14755: on SDK dApps refresh update the model that has data source from persistence instead of using reset
+ dapps.clear();
+
+ let tmpMap = {}
+ for (let key in sessions) {
+ let dapp = sessions[key].peer.metadata
+ if (dapp.icons.length > 0) {
+ dapp.iconUrl = dapp.icons[0]
}
+ tmpMap[dapp.url] = dapp;
+ }
+ // Iterate tmpMap and fill dapps
+ for (let key in tmpMap) {
+ dapps.append(tmpMap[key]);
}
});
- })();
- }
+ }
- function resetSessionsModel() {
- sessions.clear();
-
- Backpressure.debounce(this, 250, () => {
- sdk.getActiveSessions((sessionList) => {
- for (var topic of Object.keys(sessionList)) {
- sessions.append(sessionList[topic]);
+ if (root.sdk.sdkReady) {
+ getActiveSessionsFn()
+ } else {
+ let conn = root.sdk.sdkReadyChanged.connect(() => {
+ if (root.sdk.sdkReady) {
+ getActiveSessionsFn()
}
});
- })();
- }
-
- function getPairingTopicFromPairingUrl(url)
- {
- if (!url.startsWith("wc:"))
- {
- return null;
}
-
- const atIndex = url.indexOf("@");
- if (atIndex < 0)
- {
- return null;
- }
-
- return url.slice(3, atIndex);
}
}
}
\ No newline at end of file
diff --git a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml
index d380580eb9..cbd7e43182 100644
--- a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml
+++ b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml
@@ -1,6 +1,7 @@
import QtQuick 2.15
import StatusQ.Core.Theme 0.1
+import StatusQ.Core.Utils 0.1
import AppLayouts.Wallet.services.dapps 1.0
import AppLayouts.Profile.stores 1.0
@@ -10,13 +11,15 @@ import shared.popups.walletconnect 1.0
import SortFilterProxyModel 0.2
import utils 1.0
-QtObject {
+QObject {
id: root
required property WalletConnectSDK wcSDK
required property DAppsStore store
required property WalletStore walletStore
+ readonly property alias dappsModel: d.dappsProvider.dappsModel
+
readonly property var validAccounts: SortFilterProxyModel {
sourceModel: walletStore.accounts
filters: ValueFilter {
@@ -28,12 +31,12 @@ QtObject {
readonly property var flatNetworks: walletStore.flatNetworks
function pair(uri) {
- _d.acceptedSessionProposal = null
+ d.acceptedSessionProposal = null
wcSDK.pair(uri)
}
function approvePairSession(sessionProposal, approvedChainIds, approvedAccount) {
- _d.acceptedSessionProposal = sessionProposal
+ d.acceptedSessionProposal = sessionProposal
let approvedNamespaces = JSON.parse(Helpers.buildSupportedNamespaces(approvedChainIds, [approvedAccount.address]))
wcSDK.buildApprovedNamespaces(sessionProposal.params, approvedNamespaces)
}
@@ -53,7 +56,7 @@ QtObject {
target: wcSDK
function onSessionProposal(sessionProposal) {
- _d.currentSessionProposal = sessionProposal
+ d.currentSessionProposal = sessionProposal
let supportedNamespacesStr = Helpers.buildSupportedNamespacesFromModels(root.flatNetworks, root.validAccounts)
wcSDK.buildApprovedNamespaces(sessionProposal.params, JSON.parse(supportedNamespacesStr))
@@ -65,12 +68,12 @@ QtObject {
return
}
- if (_d.acceptedSessionProposal) {
- wcSDK.approveSession(_d.acceptedSessionProposal, approvedNamespaces)
+ if (d.acceptedSessionProposal) {
+ wcSDK.approveSession(d.acceptedSessionProposal, approvedNamespaces)
} else {
let res = Helpers.extractChainsAndAccountsFromApprovedNamespaces(approvedNamespaces)
- root.connectDApp(res.chains, _d.currentSessionProposal, approvedNamespaces)
+ root.connectDApp(res.chains, d.currentSessionProposal, approvedNamespaces)
}
}
@@ -79,19 +82,23 @@ QtObject {
root.approveSessionResult(session, err)
if (err) {
+ // TODO #14676: handle the error
+ console.error("Failed to approve session", err)
return
}
// TODO #14754: implement custom dApp notification
- let app_url = _d.currentSessionProposal ? _d.currentSessionProposal.params.proposer.metadata.url : "-"
+ let app_url = d.currentSessionProposal ? d.currentSessionProposal.params.proposer.metadata.url : "-"
Global.displayToastMessage(qsTr("Connected to %1 via WalletConnect").arg(app_url), "", "checkmark-circle", false, Constants.ephemeralNotificationType.success, "")
// Persist session
store.addWalletConnectSession(JSON.stringify(session))
+
+ d.dappsProvider.updateDapps()
}
function onRejectSessionResult(err) {
- let app_url = _d.currentSessionProposal ? _d.currentSessionProposal.params.proposer.metadata.url : "-"
+ let app_url = d.currentSessionProposal ? d.currentSessionProposal.params.proposer.metadata.url : "-"
if(err) {
Global.displayToastMessage(qsTr("Failed to reject connection request for %1").arg(app_url), "", "warning", false, Constants.ephemeralNotificationType.danger, "")
} else {
@@ -100,7 +107,7 @@ QtObject {
}
function onSessionDelete(topic, err) {
- let app_url = _d.currentSessionProposal ? _d.currentSessionProposal.params.proposer.metadata.url : "-"
+ let app_url = d.currentSessionProposal ? d.currentSessionProposal.params.proposer.metadata.url : "-"
if(err) {
Global.displayToastMessage(qsTr("Failed to disconnect from %1").arg(app_url), "", "warning", false, Constants.ephemeralNotificationType.danger, "")
} else {
@@ -109,12 +116,36 @@ QtObject {
}
}
- readonly property QtObject _d: QtObject {
+ QtObject {
+ id: d
+
property var currentSessionProposal: null
property var acceptedSessionProposal: null
readonly property DAppsListProvider dappsProvider: DAppsListProvider {
sdk: root.wcSDK
+ store: root.store
+ }
+
+ // 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);
}
}
+
+ Component.onCompleted: {
+ d.dappsProvider.updateDapps()
+ }
}
\ No newline at end of file
diff --git a/ui/app/AppLayouts/Wallet/stores/RootStore.qml b/ui/app/AppLayouts/Wallet/stores/RootStore.qml
index 7450d316b8..cca93870f2 100644
--- a/ui/app/AppLayouts/Wallet/stores/RootStore.qml
+++ b/ui/app/AppLayouts/Wallet/stores/RootStore.qml
@@ -56,7 +56,7 @@ QtObject {
property var activityDetailsController: walletSectionInst.activityDetailsController
property string signingPhrase: walletSectionInst.signingPhrase
property string mnemonicBackedUp: walletSectionInst.isMnemonicBackedUp
- property var walletConnectModule: walletSectionInst.walletConnectModule
+ property var walletConnectController: walletSectionInst.walletConnectController
property CollectiblesStore collectiblesStore: CollectiblesStore {}
diff --git a/ui/app/AppLayouts/Wallet/views/pocwalletconnect/POCWalletConnectModal.qml b/ui/app/AppLayouts/Wallet/views/pocwalletconnect/POCWalletConnectModal.qml
index 22fbc06d58..a4e991fff0 100644
--- a/ui/app/AppLayouts/Wallet/views/pocwalletconnect/POCWalletConnectModal.qml
+++ b/ui/app/AppLayouts/Wallet/views/pocwalletconnect/POCWalletConnectModal.qml
@@ -204,25 +204,6 @@ Popup {
}
}
- ColumnLayout {
- Layout.fillWidth: true
-
- POCSessions {
- Layout.fillWidth: true
- Layout.preferredHeight: contentHeight
-
- model: root.sdk.sessionsModel
-
- onDisconnect: function (topic) {
- root.sdk.disconnectSession(topic)
- }
-
- onPing: function (topic) {
- root.sdk.ping(topic)
- }
- }
- }
-
ColumnLayout {
Layout.fillWidth: true
@@ -230,7 +211,7 @@ Popup {
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
- model: root.sdk.pairingsModel
+ model: root.sdk.dappsModel
onDisconnect: function (topic) {
root.sdk.disconnectPairing(topic)
diff --git a/ui/app/mainui/AppMain.qml b/ui/app/mainui/AppMain.qml
index 87895ec733..4f84fd41e7 100644
--- a/ui/app/mainui/AppMain.qml
+++ b/ui/app/mainui/AppMain.qml
@@ -2044,7 +2044,7 @@ Item {
projectId: WalletStore.RootStore.appSettings.walletConnectProjectID
}
store: DAppsStore {
- module: WalletStore.RootStore.walletConnectModule
+ controller: WalletStore.RootStore.walletConnectController
}
walletStore: appMain.rootStore.profileSectionStore.walletStore
diff --git a/ui/imports/shared/popups/walletconnect/ConnectDAppModal.qml b/ui/imports/shared/popups/walletconnect/ConnectDAppModal.qml
index 998e838018..188aef9ee2 100644
--- a/ui/imports/shared/popups/walletconnect/ConnectDAppModal.qml
+++ b/ui/imports/shared/popups/walletconnect/ConnectDAppModal.qml
@@ -4,6 +4,8 @@ import QtQuick.Layouts 1.15
import QtQml.Models 2.14
import SortFilterProxyModel 0.2
+import QtGraphicalEffects 1.15
+
import StatusQ 0.1
import StatusQ.Core 0.1
import StatusQ.Popups.Dialog 0.1
@@ -41,7 +43,9 @@ StatusDialog {
dappCard.name = m.name
dappCard.url = m.url
if(m.icons.length > 0) {
- dappCard.icon = m.icons[0]
+ dappCard.iconUrl = m.icons[0]
+ } else {
+ dappCard.iconUrl = ""
}
d.dappChains.clear()
@@ -230,23 +234,39 @@ StatusDialog {
component DAppCard: ColumnLayout {
property alias name: appNameText.text
property alias url: appUrlText.text
- property alias icon: iconDisplay.asset.source
-
- // TODO: this doesn't work as expected, the icon is not displayed properly
- // TODO: set a fallback icon for when the provided icon is not available
- StatusRoundIcon {
- id: iconDisplay
+ property string iconUrl: ""
+ Rectangle {
Layout.alignment: Qt.AlignHCenter
- Layout.bottomMargin: 16
+ Layout.preferredWidth: 72
+ Layout.preferredHeight: Layout.preferredWidth
- width: 72
- height: 72
+ radius: width / 2
+ color: Theme.palette.primaryColor3
- asset.width: width
- asset.height: height
- asset.color: "transparent"
- asset.bgColor: "transparent"
+ StatusRoundedImage {
+ id: iconDisplay
+
+ anchors.fill: parent
+
+ visible: !fallbackImage.visible
+
+ image.source: iconUrl
+ }
+
+ StatusIcon {
+ id: fallbackImage
+
+ anchors.centerIn: parent
+
+ width: 40
+ height: 40
+
+ icon: "dapp"
+ color: Theme.palette.primaryColor1
+
+ visible: iconDisplay.image.isLoading || iconDisplay.image.isError || !iconUrl
+ }
}
StatusBaseText {
diff --git a/ui/imports/shared/popups/walletconnect/DAppsListPopup.qml b/ui/imports/shared/popups/walletconnect/DAppsListPopup.qml
index 606343c13e..83ea5cde80 100644
--- a/ui/imports/shared/popups/walletconnect/DAppsListPopup.qml
+++ b/ui/imports/shared/popups/walletconnect/DAppsListPopup.qml
@@ -3,8 +3,10 @@ import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15
+import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
+import StatusQ.Components 0.1
import shared.controls 1.0
@@ -13,25 +15,23 @@ Popup {
objectName: "dappsPopup"
- property int menuWidth: 312
+ required property var model
signal pairWCDapp()
- contentWidth: root.menuWidth
- contentHeight: list.height
modal: false
padding: 8
closePolicy: Popup.CloseOnEscape | Popup.CloseOnOutsideClick | Popup.CloseOnPressOutside
background: Rectangle {
- id: bckgContent
+ id: backgroundContent
color: Theme.palette.statusMenu.backgroundColor
radius: 8
layer.enabled: true
layer.effect: DropShadow {
anchors.fill: parent
- source: bckgContent
+ source: backgroundContent
horizontalOffset: 0
verticalOffset: 4
radius: 12
@@ -42,16 +42,56 @@ Popup {
}
ColumnLayout {
- id: list
- anchors.left: parent.left
- anchors.right: parent.right
- width: parent.width
+ id: mainLayout
+
+ implicitWidth: 280
+
spacing: 8
ShapeRectangle {
+ id: listPlaceholder
+
+ text: qsTr("Connected dApps will appear here")
+
Layout.fillWidth: true
Layout.preferredHeight: implicitHeight
- text: qsTr("Connected dApps will appear here")
+
+ visible: listView.count === 0
+ }
+
+ Item {
+ Layout.fillWidth: true
+ Layout.preferredHeight: 32
+ Layout.leftMargin: 8
+
+ visible: !listPlaceholder.visible
+
+ StatusBaseText {
+ text: qsTr("Connected dApps")
+
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+
+ font.pixelSize: 12
+ color: Theme.palette.baseColor1
+ }
+ }
+
+ StatusListView {
+ id: listView
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: contentHeight
+ Layout.maximumHeight: 280
+
+ model: root.model
+ visible: !listPlaceholder.visible
+
+ delegate: DAppDelegate {
+ implicitWidth: listView.width
+ }
+
+ ScrollBar.vertical: null
}
StatusButton {
@@ -64,4 +104,96 @@ Popup {
}
}
}
+
+ component DAppDelegate: Item {
+ implicitHeight: 50
+
+ required property string name
+ required property string url
+ required property string iconUrl
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.margins: 8
+
+ Item {
+ Layout.preferredWidth: 32
+ Layout.preferredHeight: 32
+
+ StatusImage {
+ id: iconImage
+
+ anchors.fill: parent
+
+ source: iconUrl
+ visible: !fallbackImage.visible
+ }
+
+ StatusIcon {
+ id: fallbackImage
+
+ anchors.fill: parent
+
+ icon: "dapp"
+ color: Theme.palette.baseColor1
+
+ visible: iconImage.isLoading || iconImage.isError || !iconUrl
+ }
+
+ layer.enabled: true
+ layer.effect: OpacityMask {
+ maskSource: Rectangle {
+ width: iconImage.width
+ height: iconImage.height
+ radius: width / 2
+ visible: false
+ }
+ }
+ }
+
+ ColumnLayout {
+ Layout.leftMargin: 12
+ Layout.rightMargin: 12
+
+ StatusBaseText {
+ text: name
+
+ Layout.fillWidth: true
+
+ font.pixelSize: 13
+ font.bold: true
+
+ elide: Text.ElideRight
+
+ clip: true
+ }
+ StatusBaseText {
+ text: url
+
+ Layout.fillWidth: true
+
+ font.pixelSize: 12
+ color: Theme.palette.baseColor1
+
+ elide: Text.ElideRight
+
+ clip: true
+ }
+ }
+
+ // TODO #14588 - Show tooltip on hover "Disconnect dApp"
+ StatusRoundButton {
+ implicitWidth: 32
+ implicitHeight: 32
+ radius: width / 2
+
+ icon.name: "disconnect"
+
+ onClicked: {
+ console.debug(`TODO #14755 - Disconnect ${name}`)
+ //root.disconnectDapp()
+ }
+ }
+ }
+ }
}
diff --git a/ui/imports/shared/stores/DAppsStore.qml b/ui/imports/shared/stores/DAppsStore.qml
index ded2ca0c60..c6270a9861 100644
--- a/ui/imports/shared/stores/DAppsStore.qml
+++ b/ui/imports/shared/stores/DAppsStore.qml
@@ -1,9 +1,28 @@
import QtQuick 2.15
QtObject {
- required property var module
+ id: root
+
+ required property var controller
+
+ /// \c dappsJson serialized from status-go.wallet.GetDapps
+ signal dappsListReceived(string dappsJson)
function addWalletConnectSession(sessionJson) {
- module.addWalletConnectSession(sessionJson)
+ controller.addWalletConnectSession(sessionJson)
+ }
+
+ /// \c getDapps triggers an async response to \c dappsListReceived
+ function getDapps() {
+ return controller.getDapps()
+ }
+
+ // Handle async response from controller
+ property Connections _connections: Connections {
+ target: controller
+
+ function onDappsListReceived(dappsJson) {
+ root.dappsListReceived(dappsJson)
+ }
}
}
\ No newline at end of file
diff --git a/vendor/status-go b/vendor/status-go
index ad9032d036..e06c490ec8 160000
--- a/vendor/status-go
+++ b/vendor/status-go
@@ -1 +1 @@
-Subproject commit ad9032d036057cf00ae2d510b475e74fb3185b43
+Subproject commit e06c490ec870a70ae72ede2b37f1235a3d903ed8