feat: changes to Network selector

This commit is contained in:
Dario Gabriel Lipicar 2025-02-06 18:00:49 -03:00
parent 8f38e10d0a
commit 1bd1021d34
No known key found for this signature in database
GPG Key ID: 9625E9494309D203
13 changed files with 238 additions and 98 deletions

View File

@ -23,6 +23,18 @@ SplitView {
orientation: Qt.Vertical
SplitView.fillWidth: true
QtObject {
id: d
property SortFilterProxyModel networksModel: SortFilterProxyModel {
sourceModel: NetworksModel.flatNetworks
filters: IndexFilter {
minimumIndex: 0
maximumIndex: singleActiveNetworkCheckBox.checked ? 0 : -1
}
}
}
Item {
id: container
@ -34,11 +46,11 @@ SplitView {
anchors.centerIn: parent
flatNetworks: NetworksModel.flatNetworks
flatNetworks: d.networksModel
multiSelection: multiSelectionCheckBox.checked
showAllSelectedText: ctrlShowAllSelectedText.checked
showTitle: ctrlShowTitle.checked
showManageNetworksButton: ctrlShowManageNetworksButton.checked
selectionAllowed: selectionAllowedCheckBox.checked
showSelectionIndicator: (ctrlShowCheckBoxes.checked && multiSelection) || (ctrlShowRadioButtons.checked && !multiSelection)
}
@ -88,9 +100,8 @@ SplitView {
}
CheckBox {
id: ctrlShowAllSelectedText
text: "Show 'All networks' text"
visible: multiSelectionCheckBox.checked
id: ctrlShowManageNetworksButton
text: "Show 'Manage networks' button"
checked: true
}
@ -100,6 +111,12 @@ SplitView {
checked: true
}
CheckBox {
id: singleActiveNetworkCheckBox
text: "Single active network"
checked: false
}
ColumnLayout {
visible: !multiSelectionCheckBox.checked
Label {

View File

@ -5,6 +5,7 @@ import QtQuick.Layouts 1.15
import StatusQ 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1
import StatusQ.Popups 0.1
import utils 1.0
@ -30,39 +31,61 @@ SplitView {
anchors.fill: parent
// Leave some space so that the popup will be opened without accounting for Layer
ColumnLayout {
Layout.maximumHeight: 50
}
RowLayout {
Layout.fillWidth: true
NetworkFilter {
id: networkFilter
// Dummy item to make space for popup
Item {
id: popupPlaceholder
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: networkSelectPopup.implicitWidth
Layout.preferredHeight: networkSelectPopup.implicitHeight
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
flatNetworks: availableNetworks
multiSelection: multiSelectionCheckbox.checked
}
NetworkSelectPopup {
id: networkSelectPopup
flatNetworks: d.activeNetworks
multiSelection: multiSelectionCheckbox.checked
showManageNetworksButton: showManageNetworksButtonCheckbox.checked
closePolicy: Popup.NoAutoClose
visible: true
Binding on selection {
value: d.selection
}
onToggleNetwork: d.toggleNetworkEnabled(chainId)
}
}
// Dummy item to make space for popup
Item {
id: popupPlaceholder
// Filler
ColumnLayout {
Layout.preferredHeight: 100
Layout.maximumHeight: 100
}
Layout.preferredWidth: networkSelectPopup.implicitWidth
Layout.preferredHeight: networkSelectPopup.implicitHeight
Item {
id: filterPlaceholder
NetworkSelectPopup {
id: networkSelectPopup
flatNetworks: availableNetworks
multiSelection: multiSelectionCheckbox.checked
closePolicy: Popup.NoAutoClose
visible: true
Layout.preferredWidth: networkFilter.implicitWidth
Layout.preferredHeight: networkFilter.implicitHeight
Layout.alignment: Qt.AlignRight | Qt.AlignTop
NetworkFilter {
id: networkFilter
flatNetworks: d.activeNetworks
multiSelection: multiSelectionCheckbox.checked
showManageNetworksButton: showManageNetworksButtonCheckbox.checked
Binding on selection {
value: d.selection
}
onToggleNetwork: d.toggleNetworkEnabled(chainId)
}
}
}
ColumnLayout {
Layout.preferredHeight: 30
Layout.maximumHeight: 30
Layout.preferredHeight: 100
Layout.maximumHeight: 100
}
RowLayout {
@ -77,7 +100,7 @@ SplitView {
ModelEntry {
id: selectedEntry
sourceModel: availableNetworks
sourceModel: d.activeNetworks
key: "chainId"
}
}
@ -97,7 +120,7 @@ SplitView {
active: false
sourceComponent: NetworkSelectPopup {
flatNetworks: availableNetworks
flatNetworks: d.activeNetworks
selection: selectedEntry.available ? [selectedEntry.value] : []
onClosed: selectPopupLoader.active = false
@ -117,7 +140,7 @@ SplitView {
}
}
Pane {
SplitView.minimumWidth: 300
SplitView.minimumWidth: 400
SplitView.fillWidth: true
SplitView.minimumHeight: 300
@ -130,7 +153,7 @@ SplitView {
Layout.fillWidth: true
Layout.fillHeight: true
model: availableNetworks
model: d.availableNetworks
delegate: ItemDelegate {
required property var model
@ -144,28 +167,26 @@ SplitView {
id: delegateRowLayout
anchors.fill: parent
Column {
ColumnLayout {
Layout.margins: 5
spacing: 3
Label { text: model.chainName }
Row {
RowLayout {
spacing: 5
Label { text: `<b>${model.shortName}</b>` }
Label { text: `ID <b>${model.chainId}</b>` }
CheckBox {
checkState: networkSelectPopup.selection.includes(model.chainId) ? Qt.Checked : Qt.Unchecked
onToggled: {
let currentSelection = networkSelectPopup.selection
if (checkState === Qt.Checked && !currentSelection.includes(model.chainId)) {
currentSelection.push(model.chainId)
} else {
currentSelection = currentSelection.filter(id => id !== model.chainId)
}
networkSelectPopup.selection = [...currentSelection]
}
text: "Enabled"
checkState: model.isEnabled ? Qt.Checked : Qt.Unchecked
onToggled: d.setIsEnabled(model.chainId, checkState === Qt.Checked)
}
CheckBox {
text: "Active"
checkState: model.isActive ? Qt.Checked : Qt.Unchecked
onToggled: d.setIsActive(model.chainId, checkState === Qt.Checked)
}
}
}
@ -181,6 +202,16 @@ SplitView {
checked: true
}
CheckBox {
id: showManageNetworksButtonCheckbox
Layout.margins: 5
text: "Show 'Manage networks' button"
checked: true
}
CheckBox {
id: testModeCheckbox
@ -201,12 +232,55 @@ SplitView {
}
}
SortFilterProxyModel {
id: availableNetworks
QtObject {
id: d
sourceModel: NetworksModel.flatNetworks
filters: ValueFilter { roleName: "isTest"; value: testModeCheckbox.checked; }
function toggleNetworkEnabled(chainId) {
let isEnabled = ModelUtils.getByKey(NetworksModel.flatNetworks, "chainId", chainId, "isEnabled")
d.setIsEnabled(chainId, !isEnabled)
}
function setIsEnabled(chainId, value) {
let index = ModelUtils.indexOf(NetworksModel.flatNetworks, "chainId", chainId)
NetworksModel.flatNetworks.setProperty(index, "isEnabled", value)
}
function setIsActive(chainId, value) {
let index = ModelUtils.indexOf(NetworksModel.flatNetworks, "chainId", chainId)
NetworksModel.flatNetworks.setProperty(index, "isActive", value)
}
readonly property var availableNetworks: SortFilterProxyModel {
sourceModel: NetworksModel.flatNetworks
filters: [
ValueFilter { roleName: "isTest"; value: testModeCheckbox.checked; }
]
}
readonly property var activeNetworks: SortFilterProxyModel {
sourceModel: d.availableNetworks
filters: [
ValueFilter { roleName: "isActive"; value: true; }
]
}
readonly property var enabledNetworks: SortFilterProxyModel {
sourceModel: d.activeNetworks
filters: [
ValueFilter { roleName: "isEnabled"; value: true; }
]
}
readonly property var chainIdsAggregator: FunctionAggregator {
model: d.enabledNetworks
initialValue: []
roleName: "chainId"
aggregateFunction: (aggr, value) => [...aggr, value]
}
readonly property var selection: d.chainIdsAggregator.value
}
}
// category: Popups

View File

@ -10,7 +10,7 @@ import Models 1.0
Item {
id: root
width: 600
height: 400
height: 600
Component {
id: componentUnderTest
@ -69,7 +69,7 @@ Item {
// 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(controlUnderTest.selection.sort(), [controlUnderTest.flatNetworks.get(0).chainId, controlUnderTest.flatNetworks.get(1).chainId, controlUnderTest.flatNetworks.get(2).chainId].sort())
compare(selectionChangedSpy.count, 4)
}

View File

@ -165,8 +165,8 @@ Item {
compare(toggleNetworkSpy.count, 1)
compare(selectionChangedSpy.count, 2)
compare(controlUnderTest.selection.length, 2)
compare(controlUnderTest.selection[0], controlUnderTest.model.get(1).chainId)
compare(controlUnderTest.selection[1], controlUnderTest.model.get(2).chainId)
verify(controlUnderTest.selection.includes(controlUnderTest.model.get(1).chainId))
verify(controlUnderTest.selection.includes(controlUnderTest.model.get(2).chainId))
compare(delegate.checkState, Qt.Checked)
delegate = findChild(controlUnderTest, "networkSelectorDelegate_" + controlUnderTest.model.get(2).chainName)
compare(delegate.checkState, Qt.Checked)

View File

@ -119,6 +119,8 @@ SettingsContentBase {
root.settingsSubSubsection === Constants.walletSettingsSubsection.manageHidden ||
root.settingsSubSubsection === Constants.walletSettingsSubsection.manageAdvanced
readonly property bool isManageNetworksSubsection: root.settingsSubSubsection === Constants.walletSettingsSubsection.manageNetworks
readonly property var walletSettings: Settings {
category: "walletSettings-" + root.myPublicKey
}
@ -143,6 +145,12 @@ SettingsContentBase {
restoreMode: Binding.RestoreNone
}
Binding on currentIndex {
value: root.networksViewIndex
when: root.settingsSubSubsection === Constants.walletSettingsSubsection.manageNetworks
restoreMode: Binding.RestoreNone
}
onCurrentIndexChanged: {
root.rootStore.backButtonName = ""
root.sectionTitle = root.walletSectionTitle

View File

@ -49,6 +49,8 @@ Item {
signal dappListRequested()
signal dappConnectRequested()
signal dappDisconnectRequested(string dappUrl)
signal manageNetworksRequested()
// TODO: remove tokenType parameter from signals below
signal sendTokenRequested(string senderAddress, string tokenId, int tokenType)
@ -272,6 +274,8 @@ Item {
onLaunchBuyCryptoModal: d.launchBuyCryptoModal()
onSendTokenRequested: root.sendTokenRequested(senderAddress, tokenId, tokenType)
onManageNetworksRequested: root.manageNetworksRequested()
}
}

View File

@ -30,12 +30,13 @@ StatusComboBox {
property bool multiSelection: true
property bool showSelectionIndicator: true
property bool showAllSelectedText: true
property bool showTitle: true
property bool selectionAllowed: true
property bool showManageNetworksButton: false
property var selection: []
signal toggleNetwork(int chainId, int index)
signal manageNetworksClicked()
onSelectionChanged: {
if (root.selection !== networkSelectorView.selection) {
@ -81,7 +82,7 @@ StatusComboBox {
Row {
id: row
spacing: -4
visible: (!d.allSelected || !root.showAllSelectedText) && chainRepeater.count > 0
visible: chainRepeater.count > 0
Repeater {
id: chainRepeater
model: SortFilterProxyModel {
@ -152,6 +153,7 @@ StatusComboBox {
selectionAllowed: root.selectionAllowed
multiSelection: root.multiSelection
showSelectionIndicator: root.showSelectionIndicator
showManageNetworksButton: root.showManageNetworksButton
selection: root.selection
onSelectionChanged: {
@ -161,6 +163,11 @@ StatusComboBox {
}
onToggleNetwork: root.toggleNetwork(chainId, index)
onManageNetworksClicked: {
control.popup.close()
root.manageNetworksClicked()
}
}
Connections {
@ -175,7 +182,6 @@ StatusComboBox {
QtObject {
id: d
readonly property int networksCount: root.flatNetworks.ModelCount.count
readonly property bool allSelected: root.selection.length === networksCount
readonly property bool noneSelected: root.selection.length === 0
readonly property bool oneSelected: root.selection.length === 1
readonly property bool selectionUnavailable: d.networksCount <= 1 && d.oneSelected
@ -198,9 +204,6 @@ StatusComboBox {
if (d.noneSelected) {
return qsTr("Select networks")
}
if (d.allSelected && root.showAllSelectedText) {
return qsTr("All networks")
}
}
return ""

View File

@ -41,6 +41,7 @@ Item {
signal dappListRequested()
signal dappConnectRequested()
signal dappDisconnectRequested(string dappUrl)
signal manageNetworksRequested()
implicitHeight: 88
@ -177,11 +178,14 @@ Item {
// network filter
NetworkFilter {
id: networkFilter
showTitle: false
showManageNetworksButton: true
Layout.alignment: Qt.AlignTop
flatNetworks: root.walletStore.filteredFlatModel
onToggleNetwork: root.walletStore.toggleNetwork(chainId)
onManageNetworksClicked: root.manageNetworksRequested()
Binding on selection {
value: chainIdsAggregator.value

View File

@ -1,9 +1,11 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15
import StatusQ 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Popups.Dialog 0.1
import SortFilterProxyModel 0.2
@ -21,9 +23,11 @@ Popup {
property bool showSelectionIndicator: true
property bool selectionAllowed: true
property bool multiSelection: false
property bool showManageNetworksButton: false
property var selection: []
signal toggleNetwork(int chainId, int index)
signal manageNetworksClicked()
onSelectionChanged: {
if (root.selection !== scrollView.selection) {
@ -51,25 +55,44 @@ Popup {
}
}
contentItem: NetworkSelectorView {
id: scrollView
contentItem: ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 4
model: root.flatNetworks
interactive: root.selectionAllowed
multiSelection: root.multiSelection
showIndicator: root.showSelectionIndicator
selection: root.selection
NetworkSelectorView {
id: scrollView
Layout.fillWidth: true
onSelectionChanged: {
if (root.selection !== scrollView.selection) {
root.selection = scrollView.selection
model: root.flatNetworks
interactive: root.selectionAllowed
multiSelection: root.multiSelection
showIndicator: root.showSelectionIndicator
selection: root.selection
onSelectionChanged: {
if (root.selection !== scrollView.selection) {
root.selection = scrollView.selection
}
}
onToggleNetwork: {
if (!root.multiSelection && root.closePolicy !== Popup.NoAutoClose)
root.close()
root.toggleNetwork(chainId, index)
}
}
onToggleNetwork: {
if (!root.multiSelection && root.closePolicy !== Popup.NoAutoClose)
root.close()
root.toggleNetwork(chainId, index)
StatusButton {
id: manageNetworksButton
visible: root.showManageNetworksButton
Layout.fillWidth: true
Layout.margins: 4
icon.name: "settings"
text: qsTr("Manage networks")
isOutline: true
onClicked: root.manageNetworksClicked()
}
}
}

View File

@ -38,26 +38,10 @@ StatusListView {
objectName: "networkSelectorList"
onSelectionChanged: {
if (!root.multiSelection && selection.length > 1) {
console.warn("Warning: Multi-selection is disabled, but multiple items are selected. Automatically selecting the first inserted item.")
selection = [selection[0]]
}
}
onSelectionChanged: d.reprocessSelection()
onMultiSelectionChanged: d.reprocessSelection()
Component.onCompleted: d.reprocessSelection()
onMultiSelectionChanged: {
if (root.multiSelection) return;
// When changing the multi-selection mode, we need to ensure that the selection is valid
if (root.selection.length > 1) {
root.selection = [root.selection[0]]
}
// Single selection defaults to first item if no selection is made
if (root.selection.length === 0 && root.count > 0) {
root.selection = [ModelUtils.get(root.model, 0).chainId]
}
}
implicitWidth: 300
implicitHeight: contentHeight
@ -99,7 +83,9 @@ StatusListView {
QtObject {
id: d
readonly property bool allSelected: root.selection.length === root.count
readonly property int networksCount: root.model.ModelCount.count
onNetworksCountChanged: d.reprocessSelection()
readonly property bool allSelected: root.selection.length === networksCount
function onToggled(initialState, chainId) {
let selection = root.selection
@ -114,12 +100,27 @@ StatusListView {
root.selection = [...selection]
}
}
Component.onCompleted: {
if(root.selection.length === 0 && !root.multiSelection && root.count > 0) {
const firstChain = ModelUtils.get(root.model, 0).chainId
root.selection = [firstChain]
function reprocessSelection() {
let selection = root.selection
if (d.networksCount === 0) {
selection = []
} else {
if (!root.multiSelection) {
// One and only one chain must be selected
if (selection.length === 0) {
selection = [ModelUtils.get(root.model, 0, "chainId")]
} else if (selection.length > 1) {
console.warn("Warning: Multi-selection is disabled, but multiple items are selected. Automatically selecting the first inserted item.")
selection = [selection[0]]
}
}
}
if (root.selection.sort().join(',') !== selection.sort().join(',')) {
root.selection = [...selection]
}
}
}
}

View File

@ -34,6 +34,7 @@ FocusScope {
signal dappListRequested()
signal dappConnectRequested()
signal dappDisconnectRequested(string dappUrl)
signal manageNetworksRequested()
ColumnLayout {
anchors.fill: parent
@ -53,6 +54,7 @@ FocusScope {
onDappListRequested: root.dappListRequested()
onDappConnectRequested: root.dappConnectRequested()
onDappDisconnectRequested: (dappUrl) =>root.dappDisconnectRequested(dappUrl)
onManageNetworksRequested: root.manageNetworksRequested()
}
Item {

View File

@ -40,6 +40,11 @@ RightTabBaseView {
signal launchSwapModal(string tokensKey)
signal sendTokenRequested(string senderAddress, string tokenId, int tokenType)
onManageNetworksRequested: {
Global.changeAppSectionBySectionType(Constants.appSection.profile,
Constants.settingsSubsection.wallet,
Constants.walletSettingsSubsection.manageNetworks)
}
function resetView() {
resetStack()

View File

@ -82,7 +82,6 @@ Rectangle {
flatNetworks: root.chainsModel
showTitle: true
multiSelection: root.multipleChainSelection
showAllSelectedText: false
selectionAllowed: false
// disable interactions w/o looking disabled