feat(dapps): show dapps list in wallet connect popup
Things done here: Integrate basic functionality for wallet connect in status-go Update the list of dapps from the SDK Retrieve the persistence dapps list from the backend as a fallback if there is no connection and SDK can't be initialized Provide a basic simple view of dapps in the wallet connect popup Closes: #14557
This commit is contained in:
parent
07bc6c49df
commit
35b81eadf6
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.}
|
||||
|
||||
|
|
|
@ -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)
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -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.}
|
|
@ -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
|
|
@ -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)
|
||||
# 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)
|
|
@ -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 ""
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -311,6 +311,7 @@
|
|||
<file>assets/img/icons/token-sale.svg</file>
|
||||
<file>assets/img/icons/token.svg</file>
|
||||
<file>assets/img/icons/destroy.svg</file>
|
||||
<file>assets/img/icons/disconnect.svg</file>
|
||||
<file>assets/img/icons/touch-id.svg</file>
|
||||
<file>assets/img/icons/travel-and-places.svg</file>
|
||||
<file>assets/img/icons/tributeToTalk.svg</file>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Feature / Disconnect">
|
||||
<path id="Union" fill-rule="evenodd" clip-rule="evenodd" d="M2.23291 3.29594C2.4928 2.9618 2.97436 2.9016 3.3085 3.16149L8.3723 7.10001C8.57572 7.25822 8.86512 7.2402 9.04734 7.05798L10.2771 5.8282C11.79 4.31537 14.0946 4.07686 15.857 5.11268C16.0734 5.23987 16.3522 5.22047 16.5297 5.04298L17.6127 3.96001C17.912 3.66068 18.3973 3.66068 18.6966 3.96001C18.996 4.25934 18.996 4.74465 18.6966 5.04398L17.6011 6.13952C17.4256 6.31499 17.4045 6.5898 17.5273 6.80541C18.5273 8.56084 18.2786 10.8344 16.7809 12.332L16.4545 12.6585C16.2371 12.8758 16.2595 13.2345 16.5021 13.4232L21.7041 17.4691C22.0382 17.729 22.0984 18.2106 21.8385 18.5447C21.5786 18.8789 21.0971 18.9391 20.7629 18.6792L2.36735 4.37154C2.03321 4.11165 1.97301 3.63009 2.23291 3.29594ZM10.4047 8.68079C10.1621 8.49209 10.1398 8.13346 10.3571 7.91612L11.3611 6.91217C12.5584 5.71485 14.4996 5.71485 15.697 6.91217C16.8943 8.10949 16.8943 10.0507 15.697 11.248L15.1447 11.8003C14.9624 11.9826 14.673 12.0006 14.4696 11.8424L10.4047 8.68079ZM12.9702 15.0408C13.2695 15.3401 13.2695 15.8254 12.9702 16.1247L11.3442 17.7507C9.84424 19.2507 7.56583 19.4979 5.80927 18.4923C5.59853 18.3716 5.3296 18.3924 5.1579 18.5641L3.30847 20.4135C3.00914 20.7129 2.52383 20.7129 2.2245 20.4135C1.92517 20.1142 1.92517 19.6289 2.2245 19.3296L4.06161 17.4925C4.23528 17.3188 4.25434 17.0461 4.12949 16.8346C3.0884 15.0711 3.32538 12.7619 4.84044 11.2469L5.74374 10.3436C6.04307 10.0442 6.52838 10.0442 6.82771 10.3436C7.12704 10.6429 7.12704 11.1282 6.82771 11.4275L5.9244 12.3309C4.72709 13.5282 4.72709 15.4694 5.9244 16.6667C7.12172 17.864 9.06296 17.864 10.2603 16.6667L11.8862 15.0408C12.1856 14.7414 12.6709 14.7414 12.9702 15.0408Z" fill="black"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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 {}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +1 @@
|
|||
Subproject commit ad9032d036057cf00ae2d510b475e74fb3185b43
|
||||
Subproject commit e06c490ec870a70ae72ede2b37f1235a3d903ed8
|
Loading…
Reference in New Issue