mirror of
https://github.com/status-im/status-desktop.git
synced 2025-01-09 22:06:25 +00:00
691de11211
Major changes: - Don't allow empty network selection. End up using the nim model directly instead because of individual row changes issues encountered with nim models - Made the clone model a generic implementation to be used in other places where we need to clone a model: ReceiveModal, AddEditSavedAddressPopup - Use cloned model as alternative to NetworksExtraStoreProxy in ReceiveModal - Added tristate support to our generic checkbox control - UX improvements as per design - Fix save address tests naming and zero address issue - Various fixes Notes: - Failed to make NetworkSelectPopup follow ground-truth: show partially checked as user intention until the network is selected in the source model. Got stuck on nim models not being stable models and report wrong entry change when reset. Tried sorting and only updating changes without reset but it didn't work. - Moved grouped property SingleSelectionInfo to its own file from an inline component after finding out that it fails to load on Linux with error "Cannot assign to property of unknown type: "*".". It works on MacOS as expected Closes: #10119
324 lines
12 KiB
QML
324 lines
12 KiB
QML
import QtQuick 2.15
|
|
import QtQuick.Controls 2.15
|
|
import QtQuick.Layouts 1.15
|
|
|
|
import StatusQ.Core 0.1
|
|
import StatusQ.Core.Theme 0.1
|
|
import StatusQ.Popups 0.1
|
|
|
|
import utils 1.0
|
|
|
|
import AppLayouts.Wallet.popups 1.0
|
|
import AppLayouts.Wallet.controls 1.0
|
|
import AppLayouts.stores 1.0
|
|
|
|
import Models 1.0
|
|
|
|
import SortFilterProxyModel 0.2
|
|
|
|
SplitView {
|
|
id: root
|
|
|
|
Pane {
|
|
SplitView.fillWidth: true
|
|
SplitView.fillHeight: true
|
|
|
|
ColumnLayout {
|
|
id: controlLayout
|
|
|
|
anchors.fill: parent
|
|
|
|
// Leave some space so that the popup will be opened without accounting for Layer
|
|
ColumnLayout {
|
|
Layout.maximumHeight: 50
|
|
}
|
|
|
|
NetworkFilter {
|
|
id: networkFilter
|
|
|
|
Layout.preferredWidth: 200
|
|
Layout.preferredHeight: 100
|
|
Layout.alignment: Qt.AlignHCenter
|
|
|
|
allNetworks: simulatedNimModel
|
|
layer1Networks: SortFilterProxyModel {
|
|
function rowData(index, propName) {
|
|
return get(index)[propName]
|
|
}
|
|
sourceModel: simulatedNimModel
|
|
filters: ValueFilter { roleName: "layer"; value: 1; }
|
|
}
|
|
layer2Networks: SortFilterProxyModel {
|
|
sourceModel: simulatedNimModel
|
|
filters: [ValueFilter { roleName: "layer"; value: 2; },
|
|
ValueFilter { roleName: "isTest"; value: false; }]
|
|
}
|
|
testNetworks: SortFilterProxyModel {
|
|
sourceModel: simulatedNimModel
|
|
filters: [ValueFilter { roleName: "layer"; value: 2; },
|
|
ValueFilter { roleName: "isTest"; value: true; }]
|
|
}
|
|
enabledNetworks: SortFilterProxyModel {
|
|
sourceModel: simulatedNimModel
|
|
filters: ValueFilter { roleName: "isEnabled"; value: true; }
|
|
}
|
|
|
|
onToggleNetwork: (network) => {
|
|
if(multiSelection) {
|
|
simulatedNimModel.toggleNetwork(network)
|
|
} else {
|
|
lastSingleSelectionLabel.text = `[${network.chainName}] (NL) - ID: ${network.chainId}, Icon: ${network.iconUrl}`
|
|
}
|
|
}
|
|
|
|
multiSelection: multiSelectionCheckbox.checked
|
|
isChainVisible: isChainVisibleCheckbox.checked
|
|
}
|
|
|
|
// Dummy item to make space for popup
|
|
Item {
|
|
id: popupPlaceholder
|
|
|
|
Layout.preferredWidth: networkSelectPopup.width
|
|
Layout.preferredHeight: networkSelectPopup.height
|
|
|
|
NetworkSelectPopup {
|
|
id: networkSelectPopup
|
|
|
|
layer1Networks: networkFilter.layer1Networks
|
|
layer2Networks: networkFilter.layer2Networks
|
|
testNetworks: networkFilter.testNetworks
|
|
|
|
useEnabledRole: false
|
|
|
|
visible: true
|
|
closePolicy: Popup.NoAutoClose
|
|
|
|
// Simulates a network toggle
|
|
onToggleNetwork: (network, networkModel, index) => simulatedNimModel.toggleNetwork(network)
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.preferredHeight: 30
|
|
Layout.maximumHeight: 30
|
|
}
|
|
|
|
RowLayout {
|
|
Button {
|
|
text: "Single Selection Popup"
|
|
onClicked: selectPopupLoader.active = true
|
|
}
|
|
Label {
|
|
id: lastSingleSelectionLabel
|
|
text: "-"
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: singleSelectionPopupPlaceholder
|
|
|
|
Layout.preferredWidth: selectPopupLoader.item ? selectPopupLoader.item.width : 0
|
|
Layout.preferredHeight: selectPopupLoader.item ? selectPopupLoader.item.height : 0
|
|
|
|
property var currentModel: networkFilter.layer2Networks
|
|
property int currentIndex: 0
|
|
|
|
Loader {
|
|
id: selectPopupLoader
|
|
|
|
active: false
|
|
|
|
sourceComponent: NetworkSelectPopup {
|
|
layer1Networks: networkFilter.layer1Networks
|
|
layer2Networks: networkFilter.layer2Networks
|
|
testNetworks: networkFilter.testNetworks
|
|
|
|
singleSelection {
|
|
enabled: true
|
|
currentModel: singleSelectionPopupPlaceholder.currentModel
|
|
currentIndex: singleSelectionPopupPlaceholder.currentIndex
|
|
}
|
|
|
|
onToggleNetwork: (network, networkModel, index) => {
|
|
lastSingleSelectionLabel.text = `[${network.chainName}] - ID: ${network.chainId}, Icon: ${network.iconUrl}`
|
|
singleSelectionPopupPlaceholder.currentModel = networkModel
|
|
singleSelectionPopupPlaceholder.currentIndex = index
|
|
}
|
|
|
|
onClosed: selectPopupLoader.active = false
|
|
}
|
|
|
|
onLoaded: item.open()
|
|
}
|
|
}
|
|
|
|
// Vertical separator
|
|
ColumnLayout {}
|
|
}
|
|
}
|
|
Pane {
|
|
SplitView.minimumWidth: 300
|
|
SplitView.fillWidth: true
|
|
SplitView.minimumHeight: 300
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
|
|
ListView {
|
|
id: allNetworksListView
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
model: simulatedNimModel
|
|
|
|
delegate: ItemDelegate {
|
|
width: allNetworksListView.width
|
|
implicitHeight: delegateRowLayout.implicitHeight
|
|
|
|
highlighted: ListView.isCurrentItem
|
|
|
|
RowLayout {
|
|
id: delegateRowLayout
|
|
anchors.fill: parent
|
|
|
|
Column {
|
|
Layout.margins: 5
|
|
|
|
spacing: 3
|
|
|
|
Label { text: model.chainName }
|
|
|
|
Row {
|
|
spacing: 5
|
|
Label { text: `<b>${model.shortName}</b>` }
|
|
Label { text: `ID <b>${model.chainId}</b>` }
|
|
CheckBox {
|
|
checkState: model.isEnabled ? Qt.Checked : Qt.Unchecked
|
|
tristate: true
|
|
nextCheckState: () => {
|
|
const nextEnabled = (checkState !== Qt.Checked)
|
|
availableNetworks.sourceModel.setProperty(availableNetworks.mapToSource(index), "isEnabled", nextEnabled)
|
|
Qt.callLater(() => { simulatedNimModel.cloneModel(availableNetworks) })
|
|
return nextEnabled ? Qt.Checked : Qt.Unchecked
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
onClicked: allNetworksListView.currentIndex = index
|
|
}
|
|
}
|
|
CheckBox {
|
|
id: multiSelectionCheckbox
|
|
|
|
Layout.margins: 5
|
|
|
|
text: "Multi Selection"
|
|
checked: true
|
|
}
|
|
|
|
CheckBox {
|
|
id: testModeCheckbox
|
|
|
|
Layout.margins: 5
|
|
|
|
text: "Test Networks Mode"
|
|
checked: false
|
|
onCheckedChanged: Qt.callLater(simulatedNimModel.cloneModel, availableNetworks)
|
|
}
|
|
|
|
CheckBox {
|
|
id: isChainVisibleCheckbox
|
|
|
|
Layout.margins: 5
|
|
|
|
text: "Is chain visible"
|
|
checked: true
|
|
}
|
|
}
|
|
}
|
|
|
|
SortFilterProxyModel {
|
|
id: availableNetworks
|
|
|
|
// Simulate Nim's way of providing access to data
|
|
function rowData(index, propName) {
|
|
return get(index)[propName]
|
|
}
|
|
|
|
sourceModel: NetworksModel.allNetworks
|
|
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)
|
|
? NetworkSelectPopup.UxEnabledState.AllEnabled
|
|
: mD.isEnabled
|
|
? NetworkSelectPopup.UxEnabledState.Enabled
|
|
: NetworkSelectPopup.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 === NetworkSelectPopup.UxEnabledState.Enabled) {
|
|
if(item.chainId !== chainId) {
|
|
chainIdOnlyEnabled = false
|
|
}
|
|
} else if(item.enabledState === NetworkSelectPopup.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 ? NetworkSelectPopup.UxEnabledState.Enabled : NetworkSelectPopup.UxEnabledState.Disabled)
|
|
} else if(chainIdOnlyEnabled || chainIdOnlyDisabled) {
|
|
simulatedNimModel.setProperty(i, "enabledState", NetworkSelectPopup.UxEnabledState.AllEnabled)
|
|
} else if(item.chainId === chainId) {
|
|
simulatedNimModel.setProperty(i, "enabledState", item.enabledState === NetworkSelectPopup.UxEnabledState.Enabled
|
|
? NetworkSelectPopup.UxEnabledState.Disabled
|
|
:NetworkSelectPopup.UxEnabledState.Enabled)
|
|
}
|
|
const haveEnabled = item.enabledState !== NetworkSelectPopup.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
|
|
}
|
|
}
|
|
}
|