feat(networkSelector): Refatoring of NetworkSelector to remove backend dependency

This commit is contained in:
Alex Jbanca 2024-06-16 00:33:12 +03:00 committed by Alex Jbanca
parent 8ca51c34cc
commit 901362dfc1
32 changed files with 491 additions and 557 deletions

View File

@ -180,12 +180,12 @@ QtObject:
break break
return networkString return networkString
proc getNetworkIds*(self: Model, shortNames: string): string = proc getNetworkIds*(self: Model, shortNames: string, areTestNetworksEnabled: bool): string =
var networkIds = "" var networkIds = ""
let networksNames = shortNames.split(":") let networksNames = shortNames.split(":")
for name in networksNames: for name in networksNames:
for item in self.delegate.getFlatNetworksList(): for item in self.delegate.getFlatNetworksList():
if item.shortName == name: if item.shortName == name and item.isTest == areTestNetworksEnabled:
networkIds = networkIds & $item.chainId & ':' networkIds = networkIds & $item.chainId & ':'
break break
return networkIds return networkIds

View File

@ -95,7 +95,7 @@ QtObject:
return self.flatNetworks.getNetworkShortNames(preferredNetworks, self.areTestNetworksEnabled) return self.flatNetworks.getNetworkShortNames(preferredNetworks, self.areTestNetworksEnabled)
proc getNetworkIds*(self: View, shortNames: string): string {.slot.} = proc getNetworkIds*(self: View, shortNames: string): string {.slot.} =
return self.flatNetworks.getNetworkIds(shortNames) return self.flatNetworks.getNetworkIds(shortNames, self.areTestNetworksEnabled)
proc getBlockExplorerURL*(self: View, chainId: int): string {.slot.} = proc getBlockExplorerURL*(self: View, chainId: int): string {.slot.} =
return self.flatNetworks.getBlockExplorerURL(chainId) return self.flatNetworks.getBlockExplorerURL(chainId)

View File

@ -54,7 +54,6 @@ SplitView {
sourceModel: NetworksModel.flatNetworks sourceModel: NetworksModel.flatNetworks
filters: ValueFilter { roleName: "isTest"; value: areTestNetworksEnabledCheckbox.checked } filters: ValueFilter { roleName: "isTest"; value: areTestNetworksEnabledCheckbox.checked }
} }
property var filteredFlatModel: networks property var filteredFlatModel: networks
property bool areTestNetworksEnabled: areTestNetworksEnabledCheckbox.checked property bool areTestNetworksEnabled: areTestNetworksEnabledCheckbox.checked
function toggleNetwork(chainId) { function toggleNetwork(chainId) {
@ -71,17 +70,6 @@ SplitView {
function updateWalletAccountPreferredChains(address, preferredChainIds) { function updateWalletAccountPreferredChains(address, preferredChainIds) {
console.warn("updateWalletAccountPreferredChains :: address ::", address, "preferredChainIds :: ", preferredChainIds) console.warn("updateWalletAccountPreferredChains :: address ::", address, "preferredChainIds :: ", preferredChainIds)
} }
function processPreferredSharingNetworkToggle(preferredSharingNetworksArray, network) {
console.warn("processPreferredSharingNetworkToggle :: preferredSharingNetworksArray ::", preferredSharingNetworksArray, "network :: ", network)
const chainId = network.chainId.toString()
if (preferredSharingNetworksArray.includes(chainId)) {
preferredSharingNetworksArray.splice(preferredSharingNetworksArray.indexOf(chainId), 1)
} else {
preferredSharingNetworksArray.push(chainId)
}
return [...preferredSharingNetworksArray]
}
} }
property var keyPairModel: WalletKeyPairModel {} property var keyPairModel: WalletKeyPairModel {}

View File

@ -2,6 +2,7 @@ import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import StatusQ.Core.Utils 0.1
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
import Storybook 1.0 import Storybook 1.0
@ -55,6 +56,11 @@ SplitView {
function createOrUpdateSavedAddress(name, address, ens, colorId, chainShortNames) { function createOrUpdateSavedAddress(name, address, ens, colorId, chainShortNames) {
logs.logEvent("createOrUpdateSavedAddress", ["name", "address", "ens", "colorId", "chainShortNames"], arguments) logs.logEvent("createOrUpdateSavedAddress", ["name", "address", "ens", "colorId", "chainShortNames"], arguments)
} }
function getNetworkIds(chainSortNames) {
let shortNames = chainSortNames.split(":").filter((shortName) => shortName.length > 0)
const chainIds = shortNames.map((shortName) => ModelUtils.getByKey(NetworksModel.flatNetworks, "shortName", shortName).chainId)
return chainIds.join(":")
}
} }
// Emulate resolving ENS by simple validation // Emulate resolving ENS by simple validation

View File

@ -46,6 +46,7 @@ SplitView {
communityName: communityName.text communityName: communityName.text
communityLogo: doodles.checked ? ModelsData.collectibles.doodles : ModelsData.collectibles.mana communityLogo: doodles.checked ? ModelsData.collectibles.doodles : ModelsData.collectibles.mana
communityColor: color1.checked ? "#FFC4E9" : "#f44336" communityColor: color1.checked ? "#FFC4E9" : "#f44336"
ownerToken.chainId: 42161
flatNetworks: SortFilterProxyModel { flatNetworks: SortFilterProxyModel {
sourceModel: NetworksModel.flatNetworks sourceModel: NetworksModel.flatNetworks

View File

@ -15,83 +15,10 @@ SplitView {
id: root id: root
Logs { id: logs } Logs { id: logs }
readonly property string ethereumName : "Ethereum Mainnet" readonly property string ethereumName : "Mainnet"
readonly property string optimismName : "Optimism" readonly property string optimismName : "Optimism"
readonly property string arbitrumName : "Arbitrum" readonly property string arbitrumName : "Arbitrum"
// Keep a clone so that the UX can be modified without affecting the original model
CloneModel {
id: simulatedNimModel
sourceModel: SortFilterProxyModel {
sourceModel: NetworksModel.flatNetworks
filters: ValueFilter { roleName: "isTest"; value: false }
}
roles: ["chainId", "layer", "chainName", "isTest", "isEnabled", "iconUrl", "shortName", "chainColor"]
rolesOverride: [{ role: "enabledState", transform: (mD) => {
return simulatedNimModel.areAllEnabled(sourceModel)
? NetworkSelectionView.UxEnabledState.AllEnabled
: mD.isEnabled
? NetworkSelectionView.UxEnabledState.Enabled
: NetworkSelectionView.UxEnabledState.Disabled
}
}]
/// Simulate the Nim model
function toggleNetwork(network) {
const chainId = network.chainId
let chainIdOnlyEnabled = true
let chainIdOnlyDisabled = true
let allEnabled = true
for (let i = 0; i < simulatedNimModel.count; i++) {
const item = simulatedNimModel.get(i)
if(item.enabledState === NetworkSelectionView.UxEnabledState.Enabled) {
if(item.chainId !== chainId) {
chainIdOnlyEnabled = false
}
} else if(item.enabledState === NetworkSelectionView.UxEnabledState.Disabled) {
if(item.chainId !== chainId) {
chainIdOnlyDisabled = false
}
allEnabled = false
} else {
if(item.chainId === chainId) {
chainIdOnlyDisabled = false
chainIdOnlyEnabled = false
}
}
}
for (let i = 0; i < simulatedNimModel.count; i++) {
const item = simulatedNimModel.get(i)
if(allEnabled) {
simulatedNimModel.setProperty(i, "enabledState", item.chainId === chainId ? NetworkSelectionView.UxEnabledState.Enabled : NetworkSelectionView.UxEnabledState.Disabled)
} else if(chainIdOnlyEnabled || chainIdOnlyDisabled) {
simulatedNimModel.setProperty(i, "enabledState", NetworkSelectionView.UxEnabledState.AllEnabled)
} else if(item.chainId === chainId) {
simulatedNimModel.setProperty(i, "enabledState", item.enabledState === NetworkSelectionView.UxEnabledState.Enabled
? NetworkSelectionView.UxEnabledState.Disabled
: NetworkSelectionView.UxEnabledState.Enabled)
}
const haveEnabled = item.enabledState !== NetworkSelectionView.UxEnabledState.Disabled
if(item.isEnabled !== haveEnabled) {
simulatedNimModel.setProperty(i, "isEnabled", haveEnabled)
}
}
}
function areAllEnabled(modelToCheck) {
for (let i = 0; i < modelToCheck.count; i++) {
if(!(modelToCheck.get(i).isEnabled)) {
return false
}
}
return true
}
}
SplitView { SplitView {
orientation: Qt.Vertical orientation: Qt.Vertical
SplitView.fillWidth: true SplitView.fillWidth: true
@ -107,30 +34,13 @@ SplitView {
anchors.centerIn: parent anchors.centerIn: parent
flatNetworks: simulatedNimModel flatNetworks: NetworksModel.flatNetworks
multiSelection: multiSelectionCheckBox.checked multiSelection: multiSelectionCheckBox.checked
showAllSelectedText: ctrlShowAllSelectedText.checked showAllSelectedText: ctrlShowAllSelectedText.checked
showTitle: ctrlShowTitle.checked showTitle: ctrlShowTitle.checked
showCheckboxes: ctrlShowCheckBoxes.checked selectionAllowed: selectionAllowedCheckBox.checked
showRadioButtons: ctrlShowRadioButtons.checked showSelectionIndicator: (ctrlShowCheckBoxes.checked && multiSelection) || (ctrlShowRadioButtons.checked && !multiSelection)
onToggleNetwork: (network) => {
logs.logEvent("onToggleNetwork: " + network.chainName)
if(multiSelection) {
simulatedNimModel.toggleNetwork(network)
} else {
if(network.chainName === root.ethereumName)
ethRadioBtn.checked = true
else if(network.chainName === root.optimismName)
optRadioBtn.checked = true
else if(network.chainName === root.arbitrumName)
arbRadioBtn.checked = true
}
}
} }
} }
@ -154,13 +64,11 @@ SplitView {
CheckBox { CheckBox {
id: multiSelectionCheckBox id: multiSelectionCheckBox
text: "Multi selection" text: "Multi selection"
checked: true checked: false
onCheckedChanged: if(!checked) ethRadioBtn.checked = true
} }
CheckBox { CheckBox {
id: ctrlShowTitle id: ctrlShowTitle
visible: !multiSelectionCheckBox.checked
text: "Show title text" text: "Show title text"
checked: true checked: true
} }
@ -186,6 +94,12 @@ SplitView {
checked: true checked: true
} }
CheckBox {
id: selectionAllowedCheckBox
text: "Selection allowed"
checked: true
}
ColumnLayout { ColumnLayout {
visible: !multiSelectionCheckBox.checked visible: !multiSelectionCheckBox.checked
Label { Label {
@ -197,19 +111,22 @@ SplitView {
id: ethRadioBtn id: ethRadioBtn
text: root.ethereumName text: root.ethereumName
onCheckedChanged: if(checked) networkFilter.setChain(NetworksModel.ethNet) checked: networkFilter.selection.includes(NetworksModel.ethNet)
onToggled: networkFilter.selection = [NetworksModel.ethNet]
} }
RadioButton { RadioButton {
id: optRadioBtn id: optRadioBtn
text: root.optimismName text: root.optimismName
onCheckedChanged: if(checked) networkFilter.setChain(NetworksModel.optimismNet) checked: networkFilter.selection.includes(NetworksModel.optimismNet)
onToggled: networkFilter.selection = [NetworksModel.optimismNet]
} }
RadioButton { RadioButton {
id: arbRadioBtn id: arbRadioBtn
text: root.arbitrumName text: root.arbitrumName
onCheckedChanged: if(checked) networkFilter.setChain(NetworksModel.arbitrumNet) checked: networkFilter.selection.includes(NetworksModel.arbitrumNet)
onToggled: networkFilter.selection = [NetworksModel.arbitrumNet]
} }
} }
} }

View File

@ -2,6 +2,7 @@ import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import StatusQ 0.1
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Popups 0.1 import StatusQ.Popups 0.1
@ -39,16 +40,7 @@ SplitView {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
flatNetworks: simulatedNimModel flatNetworks: availableNetworks
onToggleNetwork: (network) => {
if(multiSelection) {
simulatedNimModel.toggleNetwork(network)
} else {
lastSingleSelectionLabel.text = `[${network.chainName}] (NL) - ID: ${network.chainId}, Icon: ${network.iconUrl}`
}
}
multiSelection: multiSelectionCheckbox.checked multiSelection: multiSelectionCheckbox.checked
} }
@ -56,21 +48,15 @@ SplitView {
Item { Item {
id: popupPlaceholder id: popupPlaceholder
Layout.preferredWidth: networkSelectPopup.width Layout.preferredWidth: networkSelectPopup.implicitWidth
Layout.preferredHeight: networkSelectPopup.height Layout.preferredHeight: networkSelectPopup.implicitHeight
NetworkSelectPopup { NetworkSelectPopup {
id: networkSelectPopup id: networkSelectPopup
flatNetworks: availableNetworks
flatNetworks: simulatedNimModel multiSelection: multiSelectionCheckbox.checked
useEnabledRole: false
visible: true
closePolicy: Popup.NoAutoClose closePolicy: Popup.NoAutoClose
visible: true
// Simulates a network toggle
onToggleNetwork: (network, index) => simulatedNimModel.toggleNetwork(network)
} }
} }
@ -86,15 +72,21 @@ SplitView {
} }
Label { Label {
id: lastSingleSelectionLabel id: lastSingleSelectionLabel
text: "-" text: selectedEntry.available ? `[${selectedEntry.item.chainName}] - ID: ${selectedEntry.item.chainId}, Icon: ${selectedEntry.item.iconUrl}` : "-"
}
ModelEntry {
id: selectedEntry
sourceModel: availableNetworks
key: "chainId"
} }
} }
Item { Item {
id: singleSelectionPopupPlaceholder id: singleSelectionPopupPlaceholder
Layout.preferredWidth: selectPopupLoader.item ? selectPopupLoader.item.width : 0 Layout.preferredWidth: selectPopupLoader.item ? selectPopupLoader.item.implicitWidth : 0
Layout.preferredHeight: selectPopupLoader.item ? selectPopupLoader.item.height : 0 Layout.preferredHeight: selectPopupLoader.item ? selectPopupLoader.item.implicitHeight : 0
property var currentModel: networkFilter.flatNetworks property var currentModel: networkFilter.flatNetworks
property int currentIndex: 0 property int currentIndex: 0
@ -105,20 +97,15 @@ SplitView {
active: false active: false
sourceComponent: NetworkSelectPopup { sourceComponent: NetworkSelectPopup {
flatNetworks: simulatedNimModel flatNetworks: availableNetworks
selection: selectedEntry.available ? [selectedEntry.value] : []
singleSelection {
enabled: true
currentModel: singleSelectionPopupPlaceholder.currentModel
currentIndex: singleSelectionPopupPlaceholder.currentIndex
}
onToggleNetwork: (network, index) => {
lastSingleSelectionLabel.text = `[${network.chainName}] - ID: ${network.chainId}, Icon: ${network.iconUrl}`
singleSelectionPopupPlaceholder.currentIndex = index
}
onClosed: selectPopupLoader.active = false onClosed: selectPopupLoader.active = false
onSelectionChanged: {
if (selectedEntry.value !== selection[0]) {
selectedEntry.value = selection[0]
}
}
} }
onLoaded: item.open() onLoaded: item.open()
@ -143,9 +130,11 @@ SplitView {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
model: simulatedNimModel model: availableNetworks
delegate: ItemDelegate { delegate: ItemDelegate {
required property var model
width: allNetworksListView.width width: allNetworksListView.width
implicitHeight: delegateRowLayout.implicitHeight implicitHeight: delegateRowLayout.implicitHeight
@ -167,20 +156,20 @@ SplitView {
Label { text: `<b>${model.shortName}</b>` } Label { text: `<b>${model.shortName}</b>` }
Label { text: `ID <b>${model.chainId}</b>` } Label { text: `ID <b>${model.chainId}</b>` }
CheckBox { CheckBox {
checkState: model.isEnabled ? Qt.Checked : Qt.Unchecked checkState: networkSelectPopup.selection.includes(model.chainId) ? Qt.Checked : Qt.Unchecked
tristate: true onToggled: {
nextCheckState: () => { let currentSelection = networkSelectPopup.selection
const nextEnabled = (checkState !== Qt.Checked) if (checkState === Qt.Checked && !currentSelection.includes(model.chainId)) {
availableNetworks.sourceModel.setProperty(availableNetworks.mapToSource(index), "isEnabled", nextEnabled) currentSelection.push(model.chainId)
Qt.callLater(() => { simulatedNimModel.cloneModel(availableNetworks) }) } else {
return nextEnabled ? Qt.Checked : Qt.Unchecked currentSelection = currentSelection.filter(id => id !== model.chainId)
}
networkSelectPopup.selection = [...currentSelection]
} }
} }
} }
} }
} }
onClicked: allNetworksListView.currentIndex = index
} }
} }
CheckBox { CheckBox {
@ -199,7 +188,15 @@ SplitView {
text: "Test Networks Mode" text: "Test Networks Mode"
checked: false checked: false
onCheckedChanged: Qt.callLater(simulatedNimModel.cloneModel, availableNetworks) }
CheckBox {
id: allowSelection
Layout.margins: 5
text: "Allow Selection"
checked: networkSelectPopup.selectionAllowed
onToggled: networkSelectPopup.selectionAllowed = checked
} }
} }
} }
@ -210,74 +207,6 @@ SplitView {
sourceModel: NetworksModel.flatNetworks sourceModel: NetworksModel.flatNetworks
filters: ValueFilter { roleName: "isTest"; value: testModeCheckbox.checked; } filters: ValueFilter { roleName: "isTest"; value: testModeCheckbox.checked; }
} }
// Keep a clone so that the UX can be modified without affecting the original model
CloneModel {
id: simulatedNimModel
sourceModel: availableNetworks
roles: ["chainId", "layer", "chainName", "isTest", "isEnabled", "iconUrl", "shortName", "chainColor"]
rolesOverride: [{ role: "enabledState", transform: (mD) => {
return simulatedNimModel.areAllEnabled(sourceModel)
? NetworkSelectionView.UxEnabledState.AllEnabled
: mD.isEnabled
? NetworkSelectionView.UxEnabledState.Enabled
: NetworkSelectionView.UxEnabledState.Disabled
}
}]
/// Simulate the Nim model
function toggleNetwork(network) {
const chainId = network.chainId
let chainIdOnlyEnabled = true
let chainIdOnlyDisabled = true
let allEnabled = true
for (let i = 0; i < simulatedNimModel.count; i++) {
const item = simulatedNimModel.get(i)
if(item.enabledState === NetworkSelectionView.UxEnabledState.Enabled) {
if(item.chainId !== chainId) {
chainIdOnlyEnabled = false
}
} else if(item.enabledState === NetworkSelectionView.UxEnabledState.Disabled) {
if(item.chainId !== chainId) {
chainIdOnlyDisabled = false
}
allEnabled = false
} else {
if(item.chainId === chainId) {
chainIdOnlyDisabled = false
chainIdOnlyEnabled = false
}
}
}
for (let i = 0; i < simulatedNimModel.count; i++) {
const item = simulatedNimModel.get(i)
if(allEnabled) {
simulatedNimModel.setProperty(i, "enabledState", item.chainId === chainId ? NetworkSelectionView.UxEnabledState.Enabled : NetworkSelectionView.UxEnabledState.Disabled)
} else if(chainIdOnlyEnabled || chainIdOnlyDisabled) {
simulatedNimModel.setProperty(i, "enabledState", NetworkSelectionView.UxEnabledState.AllEnabled)
} else if(item.chainId === chainId) {
simulatedNimModel.setProperty(i, "enabledState", item.enabledState === NetworkSelectionView.UxEnabledState.Enabled
? NetworkSelectionView.UxEnabledState.Disabled
:NetworkSelectionView.UxEnabledState.Enabled)
}
const haveEnabled = item.enabledState !== NetworkSelectionView.UxEnabledState.Disabled
if(item.isEnabled !== haveEnabled) {
simulatedNimModel.setProperty(i, "isEnabled", haveEnabled)
}
}
}
function areAllEnabled(modelToCheck) {
for (let i = 0; i < modelToCheck.count; i++) {
if(!(modelToCheck.get(i).isEnabled)) {
return false
}
}
return true
}
}
} }
// category: Popups // category: Popups

View File

@ -74,27 +74,16 @@ SplitView {
return result return result
} }
function processPreferredSharingNetworkToggle(preferredSharingNetworks, toggledNetwork) { function getNetworkIds(chainShortNames) {
let prefChains = preferredSharingNetworks let result = ""
if(prefChains.length === filteredFlatModel.count) { if (!chainShortNames) return result
prefChains = [toggledNetwork.chainId.toString()]
let shortNames = chainShortNames.split(":").filter((shortName) => shortName.length > 0)
for(let i = 0; i< shortNames.length; i++) {
let chainId = ModelUtils.getByKey(NetworksModel.flatNetworks, "shortName", shortNames[i]).chainId
result += ":" + chainId.toString()
} }
else if(!prefChains.includes(toggledNetwork.chainId.toString())) { return result
prefChains.push(toggledNetwork.chainId.toString())
}
else {
if(prefChains.length === 1) {
prefChains = getAllNetworksChainIds()
}
else {
for(var i = 0; i < prefChains.length;i++) {
if(prefChains[i] === toggledNetwork.chainId.toString()) {
prefChains.splice(i, 1)
}
}
}
}
return prefChains
} }
function addressWasShown(account) { function addressWasShown(account) {

View File

@ -85,6 +85,7 @@ SplitView {
filters: ValueFilter { roleName: "isTest"; value: false } filters: ValueFilter { roleName: "isTest"; value: false }
} }
function toggleNetwork(chainId) { function toggleNetwork(chainId) {
print ("toggleNetwork called with chainId: " + chainId)
} }
function getAllNetworksSupportedString(hovered) { function getAllNetworksSupportedString(hovered) {

View File

@ -0,0 +1,89 @@
import QtQuick 2.14
import QtTest 1.15
import AppLayouts.Wallet.popups 1.0
import utils 1.0
import Models 1.0
Item {
id: root
width: 600
height: 400
Component {
id: componentUnderTest
NetworkSelectPopup {
anchors.centerIn: parent
flatNetworks: NetworksModel.flatNetworks
visible: true
}
}
SignalSpy {
id: selectionChangedSpy
target: controlUnderTest
signalName: "onSelectionChanged"
}
property NetworkSelectPopup controlUnderTest: null
TestCase {
name: "NetworkSelectPopup"
when: windowShown
function init() {
controlUnderTest = createTemporaryObject(componentUnderTest, root)
controlUnderTest.open()
compare(controlUnderTest.opened, true)
selectionChangedSpy.clear()
}
function test_basicGeometry() {
verify(!!controlUnderTest)
compare(controlUnderTest.width, 300)
compare(controlUnderTest.height, controlUnderTest.contentHeight + controlUnderTest.padding * 2)
}
function test_selectionBindings() {
//single selection - select using the selectio property
compare(controlUnderTest.multiSelection, false)
controlUnderTest.selection = [controlUnderTest.flatNetworks.get(0).chainId]
compare(controlUnderTest.selection, [controlUnderTest.flatNetworks.get(0).chainId])
compare(selectionChangedSpy.count, 1)
//single selection - select using the view
const secondDelegate = findChild(controlUnderTest.contentItem, "networkSelectorDelegate_" + controlUnderTest.flatNetworks.get(1).chainName)
mouseClick(secondDelegate)
compare(controlUnderTest.selection, [controlUnderTest.flatNetworks.get(1).chainId])
compare(selectionChangedSpy.count, 2)
// multi selection - select using selection property
controlUnderTest.open()
controlUnderTest.multiSelection = true
controlUnderTest.selection = [controlUnderTest.flatNetworks.get(0).chainId, controlUnderTest.flatNetworks.get(1).chainId]
compare(controlUnderTest.selection, [controlUnderTest.flatNetworks.get(0).chainId, controlUnderTest.flatNetworks.get(1).chainId])
compare(selectionChangedSpy.count, 3)
// multi selection - select using the view
const thirdDelegate = findChild(controlUnderTest.contentItem, "networkSelectorDelegate_" + controlUnderTest.flatNetworks.get(2).chainName)
mouseClick(thirdDelegate)
compare(controlUnderTest.selection, [controlUnderTest.flatNetworks.get(0).chainId, controlUnderTest.flatNetworks.get(1).chainId, controlUnderTest.flatNetworks.get(2).chainId])
compare(selectionChangedSpy.count, 4)
}
function test_closeAfterSingleSelection() {
compare(controlUnderTest.multiSelection, false)
const secondDelegate = findChild(controlUnderTest.contentItem, "networkSelectorDelegate_" + controlUnderTest.flatNetworks.get(1).chainName)
mouseClick(secondDelegate)
compare(controlUnderTest.opened, false)
controlUnderTest.open()
controlUnderTest.multiSelection = true
const thirdDelegate = findChild(controlUnderTest.contentItem, "networkSelectorDelegate_" + controlUnderTest.flatNetworks.get(2).chainName)
mouseClick(thirdDelegate)
compare(controlUnderTest.opened, true)
}
}
}

View File

@ -81,7 +81,7 @@ Item {
verify(!!delegate) verify(!!delegate)
compare(delegate.title, model.chainName) compare(delegate.title, model.chainName)
compare(delegate.iconUrl, Style.svg(model.iconUrl)) compare(delegate.iconUrl, (model.isTest ? Style.svg(model.iconUrl + "-test") : Style.svg(model.iconUrl)))
compare(delegate.showIndicator, controlUnderTest.showIndicator) compare(delegate.showIndicator, controlUnderTest.showIndicator)
compare(delegate.multiSelection, controlUnderTest.multiSelection) compare(delegate.multiSelection, controlUnderTest.multiSelection)
compare(delegate.checkState, controlUnderTest.selection.includes(model.chainId) ? Qt.Checked : Qt.Unchecked) compare(delegate.checkState, controlUnderTest.selection.includes(model.chainId) ? Qt.Checked : Qt.Unchecked)

View File

@ -100,7 +100,7 @@ QtObject {
chainId: 420, chainId: 420,
chainName: "Optimism Goerli Testnet", chainName: "Optimism Goerli Testnet",
blockExplorerUrl: "https://goerli-optimism.etherscan.io/", blockExplorerUrl: "https://goerli-optimism.etherscan.io/",
iconUrl: "network/Network=Testnet", iconUrl: "network/Network=Optimism",
chainColor: "#939BA1", chainColor: "#939BA1",
shortName: "goOpt", shortName: "goOpt",
nativeCurrencyName: "Ether", nativeCurrencyName: "Ether",
@ -128,7 +128,7 @@ QtObject {
chainId: 421613, chainId: 421613,
chainName: "Arbitrum Goerli", chainName: "Arbitrum Goerli",
blockExplorerUrl: "https://goerli.arbiscan.io/", blockExplorerUrl: "https://goerli.arbiscan.io/",
iconUrl: "network/Network=Testnet", iconUrl: "network/Network=Arbitrum",
chainColor: "#939BA1", chainColor: "#939BA1",
shortName: "goArb", shortName: "goArb",
nativeCurrencyName: "Ether", nativeCurrencyName: "Ether",

View File

@ -17,6 +17,7 @@ Item {
property alias contentItem: comboBox.contentItem property alias contentItem: comboBox.contentItem
property alias comboBoxListViewSection: listView.section property alias comboBoxListViewSection: listView.section
readonly property alias indicator: statusIndicator readonly property alias indicator: statusIndicator
property alias popup: comboBox.popup
property alias currentIndex: comboBox.currentIndex property alias currentIndex: comboBox.currentIndex
property alias currentValue: comboBox.currentValue property alias currentValue: comboBox.currentValue

View File

@ -1,6 +1,7 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Layouts 1.14 import QtQuick.Layouts 1.14
import StatusQ 0.1
import StatusQ.Components 0.1 import StatusQ.Components 0.1
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Core 0.1 import StatusQ.Core 0.1
@ -80,8 +81,6 @@ StatusScrollView {
contentWidth: mainLayout.width contentWidth: mainLayout.width
contentHeight: mainLayout.height contentHeight: mainLayout.height
Component.onCompleted: networkSelector.setChain(ownerToken.chainId)
ColumnLayout { ColumnLayout {
id: mainLayout id: mainLayout
@ -262,10 +261,6 @@ StatusScrollView {
property string label property string label
property string description property string description
function setChain(chainId) { netFilter.setChain(chainId) }
readonly property alias currentNetworkName: netFilter.currentValue
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: Style.current.padding Layout.topMargin: Style.current.padding
spacing: 8 spacing: 8
@ -282,6 +277,8 @@ StatusScrollView {
Layout.fillWidth: true Layout.fillWidth: true
flatNetworks: root.flatNetworks flatNetworks: root.flatNetworks
selection: !!ownerToken.chainId ? [ownerToken.chainId] : [SQUtils.ModelUtils.getByKey(flatNetworks, "layer", 2).chainId/*first layer 2 network*/]
multiSelection: false multiSelection: false
control.topPadding: 10 control.topPadding: 10
control.background: Rectangle { control.background: Rectangle {
@ -291,16 +288,16 @@ StatusScrollView {
border.color: Theme.palette.directColor7 border.color: Theme.palette.directColor7
} }
onToggleNetwork: (network) => { onToggleNetwork: {
// Set Owner Token network properties: // Set Owner Token network properties:
ownerToken.chainId = network.chainId ownerToken.chainId = singleSelectionItemData.chainId
ownerToken.chainName = network.chainName ownerToken.chainName = singleSelectionItemData.chainName
ownerToken.chainIcon = network.iconUrl ownerToken.chainIcon = singleSelectionItemData.iconUrl
// Set TMaster Token network properties: // Set TMaster Token network properties:
tMasterToken.chainId = network.chainId tMasterToken.chainId = singleSelectionItemData.chainId
tMasterToken.chainName = network.chainName tMasterToken.chainName = singleSelectionItemData.chainName
tMasterToken.chainIcon = network.iconUrl tMasterToken.chainIcon = singleSelectionItemData.iconUrl
} }
} }
} }

View File

@ -162,29 +162,6 @@ QtObject {
return networksModuleInst.getNetworkShortNames(chainIds) return networksModuleInst.getNetworkShortNames(chainIds)
} }
function processPreferredSharingNetworkToggle(preferredSharingNetworks, toggledNetwork) {
let prefChains = preferredSharingNetworks
if(prefChains.length === root.flatNetworks.count) {
prefChains = [toggledNetwork.chainId.toString()]
}
else if(!prefChains.includes(toggledNetwork.chainId.toString())) {
prefChains.push(toggledNetwork.chainId.toString())
}
else {
if(prefChains.length === 1) {
prefChains = getAllNetworksChainIds()
}
else {
for(var i = 0; i < prefChains.length;i++) {
if(prefChains[i] === toggledNetwork.chainId.toString()) {
prefChains.splice(i, 1)
}
}
}
}
return prefChains
}
function copyToClipboard(textToCopy) { function copyToClipboard(textToCopy) {
globalUtils.copyToClipboard(textToCopy) globalUtils.copyToClipboard(textToCopy)
} }

View File

@ -43,12 +43,8 @@ ColumnLayout {
readonly property bool privateKeyAccount: !!root.keyPair? root.keyPair.pairType === Constants.keypair.type.privateKeyImport: false readonly property bool privateKeyAccount: !!root.keyPair? root.keyPair.pairType === Constants.keypair.type.privateKeyImport: false
readonly property bool seedImport: !!root.keyPair? root.keyPair.pairType === Constants.keypair.type.seedImport: false readonly property bool seedImport: !!root.keyPair? root.keyPair.pairType === Constants.keypair.type.seedImport: false
readonly property string preferredSharingNetworks: !!root.account? root.account.preferredSharingChainIds: "" readonly property string preferredSharingNetworks: !!root.account? root.account.preferredSharingChainIds: ""
property var preferredSharingNetworksArray: preferredSharingNetworks.split(":").filter(Boolean) property var preferredSharingNetworksArray: preferredSharingNetworks.split(":").filter(Boolean).map(Number)
property string preferredSharingNetworkShortNames: walletStore.getNetworkShortNames(preferredSharingNetworks) property string preferredSharingNetworkShortNames: walletStore.getNetworkShortNames(preferredSharingNetworks)
onPreferredSharingNetworksChanged: {
preferredSharingNetworksArray = preferredSharingNetworks.split(":").filter(Boolean)
preferredSharingNetworkShortNames = walletStore.getNetworkShortNames(preferredSharingNetworks)
}
} }
spacing: 0 spacing: 0
@ -257,11 +253,15 @@ ColumnLayout {
components: [ components: [
NetworkFilter { NetworkFilter {
flatNetworks: root.walletStore.filteredFlatModel flatNetworks: root.walletStore.filteredFlatModel
preferredNetworksMode: true multiSelection: true
preferredSharingNetworks: d.preferredSharingNetworksArray selection: d.preferredSharingNetworksArray
onToggleNetwork: (network) => {
d.preferredSharingNetworksArray = root.walletStore.processPreferredSharingNetworkToggle(d.preferredSharingNetworksArray, network) onSelectionChanged: {
if (selection !== d.preferredSharingNetworksArray) {
d.preferredSharingNetworksArray = selection
} }
}
control.popup.onClosed: { control.popup.onClosed: {
if (!!root.account) { if (!!root.account) {
root.walletStore.updateWalletAccountPreferredChains(root.account.address, d.preferredSharingNetworksArray.join(":")) root.walletStore.updateWalletAccountPreferredChains(root.account.address, d.preferredSharingNetworksArray.join(":"))

View File

@ -1,4 +1,5 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQml 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
@ -15,6 +16,7 @@ import StatusQ.Controls 0.1
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
import AppLayouts.Wallet.helpers 1.0 import AppLayouts.Wallet.helpers 1.0
import AppLayouts.Wallet.popups 1.0
import utils 1.0 import utils 1.0
@ -24,70 +26,28 @@ StatusComboBox {
id: root id: root
required property var flatNetworks required property var flatNetworks
readonly property alias singleSelectionItemData: d.singleSelectionItem.item
property bool multiSelection: true property bool multiSelection: true
property bool preferredNetworksMode: false property bool showSelectionIndicator: true
property var preferredSharingNetworks: []
property bool showAllSelectedText: true property bool showAllSelectedText: true
property bool showCheckboxes: true
property bool showRadioButtons: true
property bool showTitle: true property bool showTitle: true
property bool selectionAllowed: true
property var selection: []
/// \c network is a network.model.nim entry signal toggleNetwork(int chainId, int index)
/// It is called for every toggled network if \c multiSelection is \c true
/// If \c multiSelection is \c false, it is called only for the selected network when the selection changes
signal toggleNetwork(var network)
function setChain(chainId) { onSelectionChanged: {
if(!multiSelection && !!root.flatNetworks && root.flatNetworks.count > 0) { if (root.selection !== networkSelectorView.selection) {
d.currentIndex = NetworkModelHelpers.getChainIndexByChainId(root.flatNetworks, chainId) networkSelectorView.selection = root.selection
if(d.currentIndex == -1)
d.currentIndex = NetworkModelHelpers.getChainIndexForFirstLayer2Network(root.flatNetworks)
// Notify change:
root.toggleNetwork(ModelUtils.get(root.flatNetworks, d.currentIndex))
} }
} }
QtObject {
id: d
readonly property string selectedChainName: {
root.multiSelection
NetworkModelHelpers.getChainName(root.flatNetworks, d.currentIndex)
}
readonly property string selectedIconUrl: {
root.multiSelection
NetworkModelHelpers.getChainIconUrl(root.flatNetworks, d.currentIndex)
}
readonly property bool allSelected: root.preferredNetworksMode ? root.preferredSharingNetworks.length === root.flatNetworks.count :
enabledFlatNetworks.count === root.flatNetworks.count
readonly property bool noneSelected: enabledFlatNetworks.count === 0
// Persist selection between selectPopupLoader reloads
property int currentIndex: 0
property SortFilterProxyModel enabledFlatNetworks: SortFilterProxyModel {
sourceModel: root.flatNetworks
filters: [
ValueFilter { roleName: "isEnabled"; value: true; enabled: !root.preferredNetworksMode },
FastExpressionFilter {
expression: root.preferredSharingNetworks.includes(chainId.toString())
expectedRoles: ["chainId"]
enabled: root.preferredNetworksMode
}
]
}
}
onMultiSelectionChanged: root.setChain()
control.padding: 12 control.padding: 12
control.spacing: 0 control.spacing: 0
control.rightPadding: 36 control.rightPadding: 36
control.topPadding: 7 control.topPadding: 7
control.popup.x: root.width - control.popup.width
control.popup.width: 300
control.popup.horizontalPadding: 4 control.popup.horizontalPadding: 4
control.popup.verticalPadding: 4 control.popup.verticalPadding: 4
@ -101,49 +61,46 @@ StatusComboBox {
control.indicator: SQP.StatusComboboxIndicator { control.indicator: SQP.StatusComboboxIndicator {
x: root.control.mirrored ? root.control.horizontalPadding : root.width - width - root.control.horizontalPadding x: root.control.mirrored ? root.control.horizontalPadding : root.width - width - root.control.horizontalPadding
y: root.control.topPadding + (root.control.availableHeight - height) / 2 y: root.control.topPadding + (root.control.availableHeight - height) / 2
visible: !d.selectionUnavailable
} }
control.contentItem: RowLayout { control.contentItem: RowLayout {
spacing: Style.current.padding spacing: Style.current.halfPadding
StatusSmartIdenticon { StatusSmartIdenticon {
objectName: "contentItemIcon" objectName: "contentItemIcon"
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
asset.height: 24 asset.height: 24
asset.width: 24 asset.width: 24
asset.isImage: !root.multiSelection asset.isImage: !root.multiSelection
asset.name: !root.multiSelection ? Style.svg(d.selectedIconUrl) : "" asset.name: !root.multiSelection ? Style.svg(d.singleSelectionIconUrl) : ""
active: !root.multiSelection active: !root.multiSelection
visible: active visible: active
} }
StatusBaseText {
objectName: "contentItemText"
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
font.pixelSize: Style.current.additionalTextSize
font.weight: Font.Medium
elide: Text.ElideRight
lineHeight: 24
lineHeightMode: Text.FixedHeight
verticalAlignment: Text.AlignVCenter
text: root.multiSelection ? (d.noneSelected ? qsTr("Select networks"): d.allSelected && root.showAllSelectedText ? qsTr("All networks") : "")
: (root.showTitle ? d.selectedChainName : "")
color: Theme.palette.baseColor1
visible: !!text
}
Row { Row {
id: row id: row
spacing: -4 spacing: -4
visible: (!d.allSelected || !root.showAllSelectedText) && chainRepeater.count > 0 visible: (!d.allSelected || !root.showAllSelectedText) && chainRepeater.count > 0
Repeater { Repeater {
id: chainRepeater id: chainRepeater
model: root.multiSelection ? d.enabledFlatNetworks: [] model: SortFilterProxyModel {
sourceModel: root.multiSelection ? root.flatNetworks : null
filters: FastExpressionFilter {
expression: {
root.selection
return root.selection.includes(model.chainId)
}
expectedRoles: ["chainId"]
}
}
delegate: StatusRoundedImage { delegate: StatusRoundedImage {
id: delegateItem id: delegateItem
required property var model
required property int index
width: 24 width: 24
height: 24 height: 24
image.source: Style.svg(model.iconUrl) image.source: model.isTest ? Style.svg(model.iconUrl + "-test") : Style.svg(model.iconUrl)
z: index + 1 z: index + 1
visible: root.preferredNetworksMode ? root.preferredSharingNetworks.includes(model.chainId.toString()): image.source !== ""
image.layer.enabled: index < chainRepeater.count - 1 && row.spacing < 0 image.layer.enabled: index < chainRepeater.count - 1 && row.spacing < 0
image.layer.effect: OpacityMask { image.layer.effect: OpacityMask {
@ -167,31 +124,83 @@ StatusComboBox {
} }
} }
} }
StatusBaseText {
objectName: "contentItemText"
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
font.pixelSize: Style.current.additionalTextSize
font.weight: Font.Medium
elide: Text.ElideRight
lineHeight: 24
lineHeightMode: Text.FixedHeight
verticalAlignment: Text.AlignVCenter
text: d.titleText
color: Theme.palette.baseColor1
visible: !!text
}
} }
control.popup.contentItem: NetworkSelectionView { popup: NetworkSelectPopup {
id: networkSelectorView
y: control.height + 4
x: root.width - width
flatNetworks: root.flatNetworks flatNetworks: root.flatNetworks
preferredSharingNetworks: root.preferredSharingNetworks selectionAllowed: root.selectionAllowed
preferredNetworksMode: root.preferredNetworksMode multiSelection: root.multiSelection
showCheckboxes: root.showCheckboxes showSelectionIndicator: root.showSelectionIndicator
showRadioButtons: root.showRadioButtons selection: root.selection
implicitWidth: contentWidth onSelectionChanged: {
implicitHeight: contentHeight if (root.selection !== networkSelectorView.selection) {
root.selection = networkSelectorView.selection
singleSelection { }
enabled: !root.multiSelection
currentModel: root.flatNetworks
currentIndex: d.currentIndex
} }
useEnabledRole: false onToggleNetwork: root.toggleNetwork(chainId, index)
}
onToggleNetwork: (network, index) => { Connections {
d.currentIndex = index target: control.popup
root.toggleNetwork(network) enabled: !root.multiSelection
if(singleSelection.enabled) function onOpened() {
if (d.selectionUnavailable)
control.popup.close() control.popup.close()
} }
} }
QtObject {
id: d
readonly property bool allSelected: root.selection.length === root.flatNetworks.count
readonly property bool noneSelected: root.selection.length === 0
readonly property bool oneSelected: root.selection.length === 1
readonly property bool selectionUnavailable: root.flatNetworks.count <= 1 && d.oneSelected
readonly property ModelEntry singleSelectionItem: ModelEntry {
sourceModel: d.oneSelected ? root.flatNetworks : null
key: "chainId"
value: root.selection[0] ?? -1
}
readonly property string singleSelectionIconUrl: singleSelectionItem.item.iconUrl ?? ""
readonly property string singleCelectionChainName: singleSelectionItem.item.chainName ?? ""
readonly property string titleText: {
if (d.oneSelected && root.showTitle) {
return d.singleCelectionChainName
}
if (root.multiSelection) {
if (d.noneSelected) {
return qsTr("Select networks")
}
if (d.allSelected && root.showAllSelectedText) {
return qsTr("All networks")
}
}
return ""
}
}
} }

View File

@ -43,7 +43,7 @@ StatusListItem {
leftPadding: 16 leftPadding: 16
rightPadding: 16 rightPadding: 16
statusListItemTitleArea.anchors.leftMargin: 12 statusListItemTitleArea.anchors.leftMargin: 12
highlighted: (d.checkState !== Qt.Unchecked && !showIndicator) highlighted: d.checkState !== Qt.Unchecked && !showIndicator
Binding on bgColor { Binding on bgColor {
when: highlighted && !root.sensor.containsMouse when: highlighted && !root.sensor.containsMouse

View File

@ -193,7 +193,7 @@ Rectangle {
asset.height: root.asset.height asset.height: root.asset.height
asset.width: root.asset.width asset.width: root.asset.width
asset.name: root.useLetterIdenticons ? model.text : Style.svg(model.iconUrl) asset.name: root.useLetterIdenticons ? model.text : (model.isTest ? Style.svg(model.iconUrl + "-test") : Style.svg(model.iconUrl))
asset.isImage: root.asset.isImage asset.isImage: root.asset.isImage
asset.bgColor: root.asset.bgColor asset.bgColor: root.asset.bgColor
asset.isLetterIdenticon: root.useLetterIdenticons asset.isLetterIdenticon: root.useLetterIdenticons

View File

@ -96,7 +96,7 @@ ConnectedDappsButton {
flatNetworks: root.wcService.flatNetworks flatNetworks: root.wcService.flatNetworks
onConnect: { onConnect: {
root.wcService.approvePairSession(sessionProposal, dappChains, selectedAccount) root.wcService.approvePairSession(sessionProposal, selectedChains, selectedAccount)
} }
onDecline: { onDecline: {

View File

@ -124,9 +124,28 @@ Item {
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
flatNetworks: walletStore.filteredFlatModel flatNetworks: walletStore.filteredFlatModel
onToggleNetwork: walletStore.toggleNetwork(chainId)
onToggleNetwork: (network) => { Binding on selection {
walletStore.toggleNetwork(network.chainId) value: chainIdsAggregator.value
}
FunctionAggregator {
id: chainIdsAggregator
readonly property SortFilterProxyModel enabledNetworksModel: SortFilterProxyModel{
sourceModel: walletStore.filteredFlatModel
filters: ValueFilter {
roleName: "isEnabled"
value: true
}
}
model: enabledNetworksModel
initialValue: []
roleName: "chainId"
aggregateFunction: (aggr, value) => [...aggr, value]
} }
} }
} }

View File

@ -9,8 +9,10 @@ import shared.controls 1.0
import shared.panels 1.0 import shared.panels 1.0
import shared.stores 1.0 as SharedStores import shared.stores 1.0 as SharedStores
import StatusQ 0.1
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 as StatusQUtils
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1 import StatusQ.Controls.Validators 0.1
import StatusQ.Popups 0.1 import StatusQ.Popups 0.1
@ -96,6 +98,14 @@ StatusModal {
property string storedChainShortNames: "" property string storedChainShortNames: ""
property bool chainShortNamesDirty: false property bool chainShortNamesDirty: false
property var networkSelection: []
onNetworkSelectionChanged: {
if (d.networkSelection !== networkSelectPopup.selection) {
networkSelectPopup.selection = d.networkSelection
}
}
property bool addressInputValid: d.editMode || property bool addressInputValid: d.editMode ||
addressInput.input.dirty && addressInput.input.dirty &&
d.addressInputIsAddress && d.addressInputIsAddress &&
@ -183,7 +193,7 @@ StatusModal {
d.ens = "" d.ens = ""
d.address = Constants.zeroAddress d.address = Constants.zeroAddress
d.chainShortNames = "" d.chainShortNames = ""
flatNetworksModelCopy.setEnabledNetworks([]) d.networkSelection = []
} }
d.cardsModel.clear() d.cardsModel.clear()
@ -459,11 +469,11 @@ StatusModal {
d.address = prefixAndAddress.address d.address = prefixAndAddress.address
d.chainShortNames = prefixAndAddress.prefix d.chainShortNames = prefixAndAddress.prefix
let prefixArrWithColumn = d.getPrefixArrayWithColumns(prefixAndAddress.prefix) Qt.callLater(()=> {
if (!prefixArrWithColumn) // Sync chain short names with model. This could result in removing networks from this text
prefixArrWithColumn = [] // Call it later to avoid binding loop warnings
d.networkSelection = store.getNetworkIds(d.chainShortNames).split(":").filter(Boolean).map(Number)
flatNetworksModelCopy.setEnabledNetworks(prefixArrWithColumn) })
} }
} }
@ -491,9 +501,10 @@ StatusModal {
} }
function getUnknownPrefixes(prefixes) { function getUnknownPrefixes(prefixes) {
const networksCount = root.flatNetworks.rowCount()
let unknownPrefixes = prefixes.filter(e => { let unknownPrefixes = prefixes.filter(e => {
for (let i = 0; i < flatNetworksModelCopy.count; i++) { for (let i = 0; i < networksCount; i++) {
if (e == flatNetworksModelCopy.get(i).shortName) if (e == StatusQUtils.ModelUtils.get(root.flatNetworks, i).shortName)
return false return false
} }
return true return true
@ -595,27 +606,24 @@ StatusModal {
defaultItemImageSource: "add" defaultItemImageSource: "add"
rightButtonVisible: true rightButtonVisible: true
property bool modelUpdateBlocked: false
function blockModelUpdate(value) {
modelUpdateBlocked = value
}
itemsModel: SortFilterProxyModel { itemsModel: SortFilterProxyModel {
sourceModel: flatNetworksModelCopy sourceModel: root.flatNetworks
filters: ValueFilter { filters: FastExpressionFilter {
roleName: "isEnabled" readonly property var filteredNetworks: d.networkSelection
value: true expression: {
return filteredNetworks.length > 0 && filteredNetworks.includes(model.chainId)
}
expectedRoles: ["chainId"]
} }
onCountChanged: { onCountChanged: {
if (!networkSelector.modelUpdateBlocked && d.initialized) { if (d.initialized) {
// Initially source model is empty, filter proxy is also empty, but does // Initially source model is empty, filter proxy is also empty, but does
// extra work and mistakenly overwrites d.chainShortNames property // extra work and mistakenly overwrites d.chainShortNames property
if (sourceModel.count != 0) { if (sourceModel.count != 0) {
const prefixAndAddress = Utils.splitToChainPrefixAndAddress(addressInput.plainText) const prefixAndAddress = Utils.splitToChainPrefixAndAddress(addressInput.plainText)
const syncedPrefix = addressInput.syncChainPrefixWithModel(prefixAndAddress.prefix, this) const syncedPrefix = addressInput.syncChainPrefixWithModel(prefixAndAddress.prefix, this)
d.chainShortNames = syncedPrefix if (addressInput.text !== syncedPrefix + prefixAndAddress.address)
addressInput.setPlainText(syncedPrefix + prefixAndAddress.address) addressInput.setPlainText(syncedPrefix + prefixAndAddress.address)
} }
} }
@ -624,17 +632,18 @@ StatusModal {
addButton.highlighted: networkSelectPopup.visible addButton.highlighted: networkSelectPopup.visible
addButton.onClicked: { addButton.onClicked: {
networkSelectPopup.openAtPosition(addButton.x, networkSelector.y + addButton.height + Style.current.xlPadding) networkSelectPopup.openAtPosition(addButton.x, addButton.height + Style.current.xlPadding)
} }
onItemClicked: function (item, index, mouse) { onItemClicked: function (item, index, mouse) {
// Append first item // Append first item
if (index === 0 && defaultItem.visible) if (index === 0 && defaultItem.visible)
networkSelectPopup.openAtPosition(defaultItem.x, networkSelector.y + defaultItem.height + Style.current.xlPadding) networkSelectPopup.openAtPosition(defaultItem.x, defaultItem.height + Style.current.xlPadding)
} }
onItemRightButtonClicked: function (item, index, mouse) { onItemRightButtonClicked: function (item, index, mouse) {
item.modelRef.isEnabled = !item.modelRef.isEnabled let networkSelection = [...d.networkSelection]
d.networkSelection = networkSelection.filter(n => n !== item.modelRef.chainId)
d.chainShortNamesDirty = true d.chainShortNamesDirty = true
} }
@ -659,31 +668,35 @@ StatusModal {
} }
} }
] ]
}
}
}
NetworkSelectPopup { NetworkSelectPopup {
id: networkSelectPopup id: networkSelectPopup
flatNetworks: flatNetworksModelCopy function openAtPosition(x, y) {
networkSelectPopup.x = x
networkSelectPopup.y = y
networkSelectPopup.open()
}
onToggleNetwork: (network) => { flatNetworks: root.flatNetworks
network.isEnabled = !network.isEnabled selection: d.networkSelection
multiSelection: true
onSelectionChanged: {
if (d.networkSelection !== networkSelectPopup.selection) {
d.networkSelection = networkSelectPopup.selection
d.chainShortNamesDirty = true d.chainShortNamesDirty = true
} }
}
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
function openAtPosition(xPos, yPos) {
x = xPos
y = yPos
open()
}
modal: true modal: true
dim: false dim: false
} }
}
}
}
rightButtons: [ rightButtons: [
StatusButton { StatusButton {
@ -695,21 +708,4 @@ StatusModal {
objectName: "addSavedAddress" objectName: "addSavedAddress"
} }
] ]
CloneModel {
id: flatNetworksModelCopy
sourceModel: root.flatNetworks
roles: ["layer", "chainId", "chainColor", "chainName","shortName", "iconUrl"]
rolesOverride: [{ role: "isEnabled", transform: (modelData) => Boolean(false) }]
function setEnabledNetworks(prefixArr) {
networkSelector.blockModelUpdate(true)
for (let i = 0; i < count; i++) {
// Add only those chainShortNames to the model, that have column ":" at the end, making it a valid chain prefix
setProperty(i, "isEnabled", prefixArr.includes(get(i).shortName + ":"))
}
networkSelector.blockModelUpdate(false)
}
}
} }

View File

@ -10,45 +10,31 @@ import SortFilterProxyModel 0.2
import utils 1.0 import utils 1.0
import "../stores/NetworkSelectPopup"
import "../controls" import "../controls"
import "../views" import "../views"
StatusDialog { Popup {
id: root id: root
property var flatNetworks required property var flatNetworks
property var preferredSharingNetworks: []
property bool preferredNetworksMode: false
/// Grouped properties for single selection state. \c singleSelection.enabled is \c false by default property bool showSelectionIndicator: true
/// \see SingleSelectionInfo property bool selectionAllowed: true
property alias singleSelection: d.singleSelection property bool multiSelection: false
property var selection: []
property bool useEnabledRole: true signal toggleNetwork(int chainId, int index)
/// \c network is a network.model.nim entry. \c model and \c index for the current selection onSelectionChanged: {
/// It is called for every toggled network if \c singleSelection.enabled is \c false if (root.selection !== scrollView.selection) {
/// If \c singleSelection.enabled is \c true, it is called only for the selected network when the selection changes scrollView.selection = root.selection
/// \see SingleSelectionInfo }
signal toggleNetwork(var network, int index)
QtObject {
id: d
property SingleSelectionInfo singleSelection: SingleSelectionInfo {}
} }
modal: false modal: false
standardButtons: Dialog.NoButton
anchors.centerIn: undefined
padding: 4 padding: 4
width: 360 implicitWidth: 300
implicitHeight: Math.min(432, scrollView.contentHeight + root.padding * 2)
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
background: Rectangle { background: Rectangle {
radius: Style.current.radius radius: Style.current.radius
@ -65,20 +51,25 @@ StatusDialog {
} }
} }
NetworkSelectionView { contentItem: NetworkSelectorView {
id: scrollView id: scrollView
width: parent.width
height: parent.height model: root.flatNetworks
anchors.fill: parent interactive: root.selectionAllowed
flatNetworks: root.flatNetworks multiSelection: root.multiSelection
preferredNetworksMode: root.preferredNetworksMode showIndicator: root.showSelectionIndicator
preferredSharingNetworks: root.preferredSharingNetworks selection: root.selection
useEnabledRole: root.useEnabledRole
singleSelection: d.singleSelection onSelectionChanged: {
onToggleNetwork: (network, index) => { if (root.selection !== scrollView.selection) {
root.toggleNetwork(network, index) root.selection = scrollView.selection
if(d.singleSelection.enabled) }
close() }
onToggleNetwork: {
if (!root.multiSelection && root.closePolicy !== Popup.NoAutoClose)
root.close()
root.toggleNetwork(chainId, index)
} }
} }
} }

View File

@ -45,7 +45,7 @@ StatusModal {
signal updatePreferredChains(string address, string preferredChains) signal updatePreferredChains(string address, string preferredChains)
onSelectedAccountChanged: { onSelectedAccountChanged: {
d.preferredChainIdsArray = root.selectedAccount.preferredSharingChainIds.split(":").filter(Boolean) d.preferredChainIdsArray = root.selectedAccount.preferredSharingChainIds.split(":").filter(Boolean).map(Number)
} }
width: 556 width: 556
@ -158,7 +158,15 @@ StatusModal {
readonly property bool multiChainView: tabBar.currentIndex === 1 readonly property bool multiChainView: tabBar.currentIndex === 1
readonly property int advanceFooterHeight: 88 readonly property int advanceFooterHeight: 88
property var preferredChainIdsArray: root.selectedAccount.preferredSharingChainIds.split(":").filter(Boolean) property var preferredChainIdsArray: []
Binding on preferredChainIdsArray {
value: root.selectedAccount.preferredSharingChainIds.split(":").filter(Boolean).map(Number)
}
onPreferredChainIdsArrayChanged: {
if (preferredChainIdsArray !== selectPopup.selection) {
selectPopup.selection = preferredChainIdsArray
}
}
property var preferredChainIds: d.preferredChainIdsArray.join(":") property var preferredChainIds: d.preferredChainIdsArray.join(":")
readonly property string preferredChainShortNames: d.multiChainView? root.getNetworkShortNames(d.preferredChainIds) : "" readonly property string preferredChainShortNames: d.multiChainView? root.getNetworkShortNames(d.preferredChainIds) : ""
@ -274,8 +282,8 @@ StatusModal {
enabled: false enabled: false
button.visible: false button.visible: false
title: model.shortName title: model.shortName
asset.name: Style.svg("tiny/" + model.iconUrl) asset.name: model.isTest ? Style.svg(model.iconUrl + "-test") : Style.svg(model.iconUrl)
visible: d.preferredChainIdsArray.includes(model.chainId.toString()) visible: d.preferredChainIdsArray.includes(model.chainId)
} }
} }
} }
@ -302,15 +310,14 @@ StatusModal {
margins: -1 // to allow positioning outside the bounds of the dialog margins: -1 // to allow positioning outside the bounds of the dialog
flatNetworks: root.store.filteredFlatModel flatNetworks: root.store.filteredFlatModel
preferredNetworksMode: true selection: d.preferredChainIdsArray
preferredSharingNetworks: d.preferredChainIdsArray multiSelection: true
useEnabledRole: false
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
onToggleNetwork: (network, index) => { onSelectionChanged: {
d.preferredChainIdsArray = store.processPreferredSharingNetworkToggle(d.preferredChainIdsArray, network) if (selection !== d.preferredChainIdsArray)
d.preferredChainIdsArray = selection
} }
onClosed: { onClosed: {

View File

@ -119,15 +119,14 @@ StatusDialog {
objectName: "networkFilter" objectName: "networkFilter"
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
multiSelection: false multiSelection: false
showRadioButtons: false showSelectionIndicator: false
showTitle: false showTitle: false
flatNetworks: root.swapAdaptor.filteredFlatNetworksModel flatNetworks: root.swapAdaptor.filteredFlatNetworksModel
onToggleNetwork: (network) => { selection: [root.swapInputParamsForm.selectedNetworkChainId]
root.swapInputParamsForm.selectedNetworkChainId = network.chainId onSelectionChanged: {
if (root.swapInputParamsForm.selectedNetworkChainId !== selection[0]) {
root.swapInputParamsForm.selectedNetworkChainId = selection[0]
} }
Component.onCompleted: {
if(root.swapInputParamsForm.selectedNetworkChainId !== -1)
networkFilter.setChain(root.swapInputParamsForm.selectedNetworkChainId)
} }
} }
@ -141,7 +140,7 @@ StatusDialog {
Connections { Connections {
target: root.swapInputParamsForm target: root.swapInputParamsForm
function onSelectedNetworkChainIdChanged() { function onSelectedNetworkChainIdChanged() {
networkFilter.setChain(root.swapInputParamsForm.selectedNetworkChainId) networkFilter.selection = [root.swapInputParamsForm.selectedNetworkChainId]
} }
} }
} }

View File

@ -1,8 +0,0 @@
import QtQml 2.15
/// Inline component was failing on Linux with "Cannot assign to property of unknown type" so we need to use a separate file for it.
QtObject {
property bool enabled: false
property var currentModel
property int currentIndex: 0
}

View File

@ -474,29 +474,6 @@ QtObject {
} }
} }
function processPreferredSharingNetworkToggle(preferredSharingNetworks, toggledNetwork) {
let prefChains = preferredSharingNetworks
if(prefChains.length === root.filteredFlatModel.count) {
prefChains = [toggledNetwork.chainId.toString()]
}
else if(!prefChains.includes(toggledNetwork.chainId.toString())) {
prefChains.push(toggledNetwork.chainId.toString())
}
else {
if(prefChains.length === 1) {
prefChains = getAllNetworksChainIds()
}
else {
for(var i = 0; i < prefChains.length;i++) {
if(prefChains[i] === toggledNetwork.chainId.toString()) {
prefChains.splice(i, 1)
}
}
}
}
return prefChains
}
function updateWatchAccountHiddenFromTotalBalance(address, hideFromTotalBalance) { function updateWatchAccountHiddenFromTotalBalance(address, hideFromTotalBalance) {
walletSectionAccounts.updateWatchAccountHiddenFromTotalBalance(address, hideFromTotalBalance) walletSectionAccounts.updateWatchAccountHiddenFromTotalBalance(address, hideFromTotalBalance)
} }

View File

@ -21,6 +21,7 @@ StatusListView {
chainId [int] - chain unique identifier chainId [int] - chain unique identifier
iconUrl [string] - SVG icon name. e.g. "network/Network=Ethereum" iconUrl [string] - SVG icon name. e.g. "network/Network=Ethereum"
layer [int] - chain layer. e.g. 1 or 2 layer [int] - chain layer. e.g. 1 or 2
isTest [bool] - true if the chain is a testnet
**/ **/
property bool showIndicator: true property bool showIndicator: true
property bool multiSelection: false property bool multiSelection: false
@ -71,7 +72,7 @@ StatusListView {
height: 48 height: 48
width: ListView.view.width width: ListView.view.width
title: model.chainName title: model.chainName
iconUrl: Style.svg(model.iconUrl) iconUrl: model.isTest ? Style.svg(model.iconUrl + "-test") : Style.svg(model.iconUrl)
showIndicator: root.showIndicator showIndicator: root.showIndicator
multiSelection: root.multiSelection multiSelection: root.multiSelection
interactive: root.interactive interactive: root.interactive
@ -100,7 +101,8 @@ StatusListView {
required property int section required property int section
width: parent.width width: parent.width
height: active ? 44 : 0 height: active ? 44 : 0
sourceComponent: section === 2 ? layer2text: null active: section === 2
sourceComponent: layer2text
Component { Component {
id: layer2text id: layer2text

View File

@ -0,0 +1,15 @@
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12.8086" cy="12" r="12" fill="#858CCC"/>
<g clip-path="url(#clip0_722_225208)">
<path d="M14.1708 13.3962L13.2802 15.8398C13.2582 15.906 13.2582 15.9796 13.2802 16.0532L14.8111 20.2558L16.5849 19.2327L14.4578 13.3962C14.4063 13.2638 14.2223 13.2638 14.1708 13.3962Z" fill="#9DA3D6"/>
<path d="M15.9588 9.29077C15.9073 9.15829 15.7233 9.15829 15.6718 9.29077L14.7812 11.7343C14.7591 11.8005 14.7591 11.8741 14.7812 11.9477L17.291 18.8293L19.0647 17.8063L15.9588 9.29813V9.29077Z" fill="#9DA3D6"/>
<path d="M12.8092 3.93767C12.8533 3.93767 12.8975 3.9524 12.9343 3.97448L19.6908 7.87528C19.7717 7.91944 19.8159 8.00776 19.8159 8.09608V15.8977C19.8159 15.986 19.7644 16.0743 19.6908 16.1185L12.9343 20.0193C12.8975 20.0414 12.8533 20.0561 12.8092 20.0561C12.765 20.0561 12.7209 20.0414 12.6841 20.0193L5.92758 16.1185C5.84662 16.0743 5.80246 15.986 5.80246 15.8977V8.08872C5.80246 8.0004 5.85398 7.91208 5.92758 7.86792L12.6841 3.96712C12.7209 3.94503 12.765 3.93032 12.8092 3.93032V3.93767ZM12.8092 2.79688C12.5663 2.79688 12.3308 2.85576 12.11 2.98088L5.3535 6.88168C4.91926 7.13192 4.6543 7.58823 4.6543 8.08872V15.8903C4.6543 16.3908 4.91926 16.8471 5.3535 17.0974L12.11 20.9982C12.3234 21.1233 12.5663 21.1822 12.8092 21.1822C13.0521 21.1822 13.2876 21.1233 13.5084 20.9982L20.2649 17.0974C20.6991 16.8471 20.9641 16.3908 20.9641 15.8903V8.08872C20.9641 7.58823 20.6991 7.13192 20.2649 6.88168L13.501 2.98088C13.2876 2.85576 13.0447 2.79688 12.8018 2.79688H12.8092Z" fill="#CED1EB"/>
<path d="M12.2351 7.53861H10.5202C10.3951 7.53861 10.2773 7.61957 10.2332 7.73733L6.56055 17.8058L8.33431 18.8288L12.3823 7.73733C12.4191 7.63429 12.3455 7.53125 12.2425 7.53125L12.2351 7.53861Z" fill="white"/>
<path d="M15.2381 7.53861H13.5233C13.3981 7.53861 13.2804 7.61957 13.2362 7.73733L9.04102 19.2337L10.8148 20.2567L15.378 7.73733C15.4148 7.63429 15.3412 7.53125 15.2381 7.53125V7.53861Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_722_225208">
<rect width="18.4" height="18.4" fill="white" transform="translate(3.6084 2.80078)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,9 @@
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.8086 24C19.436 24 24.8086 18.6274 24.8086 12C24.8086 5.37258 19.436 0 12.8086 0C6.18118 0 0.808594 5.37258 0.808594 12C0.808594 18.6274 6.18118 24 12.8086 24Z" fill="#858CCC"/>
<path d="M13.1826 3V9.65282L18.8054 12.1657L13.1826 3Z" fill="white" fill-opacity="0.602"/>
<path d="M13.1833 3L7.56055 12.1657L13.1833 9.65282V3Z" fill="white"/>
<path d="M13.1826 16.475V20.9955L18.8086 13.2109L13.1826 16.475Z" fill="white" fill-opacity="0.602"/>
<path d="M13.1823 20.9955V16.475L7.55957 13.2109L13.1823 20.9955Z" fill="white"/>
<path d="M13.1826 15.4293L18.8054 12.1652L13.1826 9.65234V15.4293Z" fill="white" fill-opacity="0.2"/>
<path d="M7.56055 12.1652L13.1833 15.4293V9.65234L7.56055 12.1652Z" fill="white" fill-opacity="0.602"/>
</svg>

After

Width:  |  Height:  |  Size: 846 B

View File

@ -0,0 +1,5 @@
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.8086 24C19.436 24 24.8086 18.6274 24.8086 12C24.8086 5.37258 19.436 0 12.8086 0C6.18118 0 0.808594 5.37258 0.808594 12C0.808594 18.6274 6.18118 24 12.8086 24Z" fill="#858CCC"/>
<path d="M9.30958 15.1879C8.59438 15.1879 8.00878 15.0199 7.55278 14.6839C7.10158 14.3431 6.87598 13.8535 6.87598 13.2247C6.87598 13.0903 6.89038 12.9319 6.91918 12.7399C6.99598 12.3079 7.10638 11.7895 7.25038 11.1799C7.65838 9.52872 8.71438 8.70312 10.4136 8.70312C10.8744 8.70312 11.292 8.77992 11.6568 8.93832C12.0216 9.08712 12.3096 9.31752 12.5208 9.62472C12.732 9.92712 12.8376 10.2871 12.8376 10.7047C12.8376 10.8295 12.8232 10.9879 12.7944 11.1799C12.7032 11.7127 12.5976 12.2359 12.468 12.7399C12.2568 13.5607 11.8968 14.1799 11.3784 14.5879C10.8648 14.9911 10.1736 15.1879 9.30958 15.1879ZM9.43918 13.8919C9.77518 13.8919 10.0584 13.7911 10.2936 13.5943C10.5336 13.3975 10.7064 13.0951 10.8072 12.6823C10.9464 12.1159 11.052 11.6263 11.124 11.2039C11.148 11.0791 11.1624 10.9495 11.1624 10.8151C11.1624 10.2679 10.8792 9.99432 10.308 9.99432C9.97198 9.99432 9.68398 10.0951 9.44398 10.2919C9.20878 10.4887 9.04078 10.7911 8.93998 11.2039C8.82958 11.6071 8.72398 12.0967 8.61358 12.6823C8.58958 12.8023 8.57518 12.9271 8.57518 13.0615C8.57038 13.6183 8.86318 13.8919 9.43918 13.8919Z" fill="white"/>
<path d="M13.2544 15.1002C13.1872 15.1002 13.1392 15.081 13.1008 15.0378C13.072 14.9898 13.0624 14.937 13.072 14.8746L14.3152 9.01857C14.3248 8.95137 14.3584 8.89857 14.416 8.85537C14.4688 8.81217 14.5264 8.79297 14.5888 8.79297H16.984C17.6512 8.79297 18.184 8.93217 18.5872 9.20577C18.9952 9.48417 19.2016 9.88257 19.2016 10.4058C19.2016 10.5546 19.1824 10.713 19.1488 10.8762C19 11.5674 18.6976 12.0762 18.2368 12.4074C17.7856 12.7386 17.1664 12.9018 16.3792 12.9018H15.1648L14.752 14.8746C14.7376 14.9418 14.7088 14.9946 14.6512 15.0378C14.5984 15.081 14.5408 15.1002 14.4784 15.1002H13.2544ZM16.4416 11.6586C16.696 11.6586 16.912 11.5914 17.0992 11.4522C17.2912 11.313 17.416 11.1162 17.4784 10.857C17.4976 10.7562 17.5072 10.665 17.5072 10.5882C17.5072 10.4154 17.4544 10.281 17.3536 10.1898C17.2528 10.0938 17.0752 10.0458 16.8304 10.0458H15.7504L15.4096 11.6586H16.4416Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -8,6 +8,7 @@ import QtGraphicalEffects 1.15
import StatusQ 0.1 import StatusQ 0.1
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Utils 0.1
import StatusQ.Popups.Dialog 0.1 import StatusQ.Popups.Dialog 0.1
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Components 0.1 import StatusQ.Components 0.1
@ -31,6 +32,7 @@ StatusDialog {
required property var flatNetworks required property var flatNetworks
readonly property alias selectedAccount: d.selectedAccount readonly property alias selectedAccount: d.selectedAccount
readonly property alias selectedChains: d.selectedChains
readonly property int notConnectedStatus: 0 readonly property int notConnectedStatus: 0
readonly property int connectionSuccessfulStatus: 1 readonly property int connectionSuccessfulStatus: 1
@ -206,14 +208,21 @@ StatusDialog {
Layout.fillWidth: true Layout.fillWidth: true
} }
// TODO: replace with a specialized network selection control
NetworkFilter { NetworkFilter {
id: networkFilter
Layout.preferredWidth: accountsDropdown.Layout.preferredWidth Layout.preferredWidth: accountsDropdown.Layout.preferredWidth
flatNetworks: d.filteredChains flatNetworks: d.filteredChains
showAllSelectedText: false showTitle: true
showCheckboxes: false multiSelection: true
enabled: d.connectionStatus === root.notConnectedStatus selectionAllowed: d.connectionStatus === root.notConnectedStatus && d.allChainIdsAggregator.value.length > 1
selection: d.selectedChains
onSelectionChanged: {
if (d.selectedChains !== networkFilter.selection) {
d.selectedChains = networkFilter.selection
}
}
} }
} }
} }
@ -372,6 +381,7 @@ StatusDialog {
} }
property var selectedAccount: ({}) property var selectedAccount: ({})
property var selectedChains: allChainIdsAggregator.value
readonly property var filteredChains: LeftJoinModel { readonly property var filteredChains: LeftJoinModel {
leftModel: d.dappChains leftModel: d.dappChains
@ -380,6 +390,14 @@ StatusDialog {
joinRole: "chainId" joinRole: "chainId"
} }
readonly property FunctionAggregator allChainIdsAggregator: FunctionAggregator {
model: d.filteredChains
initialValue: []
roleName: "chainId"
aggregateFunction: (aggr, value) => [...aggr, value]
}
readonly property var dappChains: ListModel {} readonly property var dappChains: ListModel {}
property int connectionStatus: notConnectedStatus property int connectionStatus: notConnectedStatus