feat: waku2 store

- add toggle to enable store functionality
- output messages to csv
- add custom waku2 nodes
This commit is contained in:
Richard Ramos 2022-08-02 14:37:27 -04:00 committed by Jonathan Rainville
parent 810a6bb5f5
commit fb526840a4
26 changed files with 648 additions and 91 deletions

View File

@ -64,7 +64,7 @@ proc getMailservers*(self: FleetConfiguration, fleet: Fleet, isWakuV2: bool): Ta
# TODO: If using wakuV2, this assumes that Waku nodes in fleet.status.json are also store nodes.
# Maybe it make senses to add a "waku-store" section in case we want to have separate node types?
# Discuss with @iurimatias, @cammellos and Vac team
let fleetKey = if isWakuV2: $FleetNodes.Waku else: $FleetNodes.Mailservers
let fleetKey = if isWakuV2: $FleetNodes.TCP_P2P_Waku else: $FleetNodes.Mailservers
if not self.fleet[$fleet].hasKey(fleetKey) :
result = initTable[string,string]()
return

View File

@ -58,6 +58,18 @@ proc setBloomLevel*(self: Controller, bloomLevel: string) =
self.delegate.onBloomLevelSet()
method toggleWakuV2Store*(self: Controller) =
let enabled = self.nodeConfigurationService.isWakuV2StoreEnabled()
if (not self.nodeConfigurationService.setWakuV2StoreEnabled(not enabled)):
# in the future we may do a call from here to show a popup about this error
error "an error occurred, we couldn't enable community history archive support"
return
self.delegate.onWakuV2StoreToggled()
method isWakuV2StoreEnabled*(self: Controller): bool =
return self.nodeConfigurationService.isWakuV2StoreEnabled()
proc getWakuV2LightClientEnabled*(self: Controller): bool =
return self.nodeConfigurationService.getV2LightMode()

View File

@ -21,6 +21,9 @@ method onFleetSet*(self: AccessInterface) {.base.} =
method onBloomLevelSet*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onWakuV2StoreToggled*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onWakuV2LightClientSet*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
@ -92,3 +95,9 @@ method toggleNodeManagementSection*(self: AccessInterface) {.base.} =
method enableDeveloperFeatures*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method isWakuV2StoreEnabled*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method toggleWakuV2Store*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -130,3 +130,12 @@ method toggleCommunitiesPortalSection*(self: Module) =
method toggleNodeManagementSection*(self: Module) =
self.controller.toggleNodeManagementSection()
method onWakuV2StoreToggled*(self: Module) =
self.view.emitWakuV2StoreEnabledSignal()
method toggleWakuV2Store*(self: Module) =
self.controller.toggleWakuV2Store()
method isWakuV2StoreEnabled*(self: Module): bool =
self.controller.isWakuV2StoreEnabled()

View File

@ -119,3 +119,16 @@ QtObject:
proc toggleCommunitiesPortalSection*(self: View) {.slot.} =
self.delegate.toggleCommunitiesPortalSection()
proc isWakuV2StoreEnabledChanged*(self: View) {.signal.}
proc getIsWakuV2StoreEnabled*(self: View): bool {.slot.} =
return self.delegate.isWakuV2StoreEnabled()
QtProperty[bool] isWakuV2StoreEnabled:
read = getIsWakuV2StoreEnabled
notify = isWakuV2StoreEnabledChanged
proc emitWakuV2StoreEnabledSignal*(self: View) =
self.isWakuV2StoreEnabledChanged()
proc toggleWakuV2Store*(self: View) {.slot.} =
self.delegate.toggleWakuV2Store()

View File

@ -65,9 +65,15 @@ method getDevicesModule*(self: AccessInterface): QVariant {.base.} =
method syncModuleDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method wakuModuleDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method getSyncModule*(self: AccessInterface): QVariant {.base.} =
raise newException(ValueError, "No implementation available")
method getWakuModule*(self: AccessInterface): QVariant {.base.} =
raise newException(ValueError, "No implementation available")
method notificationsModuleDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -32,6 +32,7 @@ import ./about/module as about_module
import ./advanced/module as advanced_module
import ./devices/module as devices_module
import ./sync/module as sync_module
import ./waku/module as waku_module
import ./notifications/module as notifications_module
import ./ens_usernames/module as ens_usernames_module
import ./communities/module as communities_module
@ -55,6 +56,7 @@ type
advancedModule: advanced_module.AccessInterface
devicesModule: devices_module.AccessInterface
syncModule: sync_module.AccessInterface
wakuModule: waku_module.AccessInterface
notificationsModule: notifications_module.AccessInterface
ensUsernamesModule: ens_usernames_module.AccessInterface
communitiesModule: communities_module.AccessInterface
@ -98,6 +100,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface,
result.advancedModule = advanced_module.newModule(result, events, settingsService, stickersService, nodeConfigurationService)
result.devicesModule = devices_module.newModule(result, events, settingsService, devicesService)
result.syncModule = sync_module.newModule(result, events, settingsService, nodeConfigurationService, mailserversService)
result.wakuModule = waku_module.newModule(result, events, settingsService, nodeConfigurationService)
result.notificationsModule = notifications_module.newModule(result, events, settingsService, chatService, contactsService)
result.ensUsernamesModule = ens_usernames_module.newModule(
result, events, settingsService, ensService, walletAccountService, networkService, tokenService
@ -117,6 +120,7 @@ method delete*(self: Module) =
self.advancedModule.delete
self.devicesModule.delete
self.syncModule.delete
self.wakuModule.delete
self.communitiesModule.delete
self.keycardModule.delete
@ -134,6 +138,7 @@ method load*(self: Module) =
self.advancedModule.load()
self.devicesModule.load()
self.syncModule.load()
self.wakuModule.load()
self.notificationsModule.load()
self.ensUsernamesModule.load()
self.communitiesModule.load()
@ -167,6 +172,9 @@ proc checkIfModuleDidLoad(self: Module) =
if(not self.syncModule.isLoaded()):
return
if(not self.wakuModule.isLoaded()):
return
if(not self.notificationsModule.isLoaded()):
return
@ -227,9 +235,15 @@ method getDevicesModule*(self: Module): QVariant =
method syncModuleDidLoad*(self: Module) =
self.checkIfModuleDidLoad()
method wakuModuleDidLoad*(self: Module) =
self.checkIfModuleDidLoad()
method getSyncModule*(self: Module): QVariant =
self.syncModule.getModuleAsVariant()
method getWakuModule*(self: Module): QVariant =
self.wakuModule.getModuleAsVariant()
method notificationsModuleDidLoad*(self: Module) =
self.checkIfModuleDidLoad()

View File

@ -51,6 +51,11 @@ QtObject:
QtProperty[QVariant] syncModule:
read = getSyncModule
proc getWakuModule(self: View): QVariant {.slot.} =
return self.delegate.getWakuModule()
QtProperty[QVariant] wakuModule:
read = getWakuModule
proc getNotificationsModule(self: View): QVariant {.slot.} =
return self.delegate.getNotificationsModule()
QtProperty[QVariant] notificationsModule:

View File

@ -0,0 +1,39 @@
import Tables, chronicles
import io_interface
import ../../../../core/eventemitter
import ../../../../core/fleets/fleet_configuration
import ../../../../../app_service/service/settings/service as settings_service
import ../../../../../app_service/service/node_configuration/service as node_configuration_service
logScope:
topics = "profile-section-waku-module-controller"
type
Controller* = ref object of RootObj
delegate: io_interface.AccessInterface
events: EventEmitter
settingsService: settings_service.Service
nodeConfigurationService: node_configuration_service.Service
proc newController*(delegate: io_interface.AccessInterface,
events: EventEmitter,
settingsService: settings_service.Service,
nodeConfigurationService: node_configuration_service.Service): Controller =
result = Controller()
result.delegate = delegate
result.events = events
result.settingsService = settingsService
result.nodeConfigurationService = nodeConfigurationService
proc delete*(self: Controller) =
discard
proc init*(self: Controller) =
discard
proc getAllWakuNodes*(self: Controller): seq[string] =
return self.nodeConfigurationService.getAllWakuNodes()
proc saveNewWakuNode*(self: Controller, nodeAddress: string) =
self.nodeConfigurationService.saveNewWakuNode(nodeAddress)

View File

@ -0,0 +1,25 @@
import NimQml
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
method delete*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method load*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method isLoaded*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} =
raise newException(ValueError, "No implementation available")
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method isAutomaticSelection*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method saveNewWakuNode*(self: AccessInterface, nodeAddress: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -0,0 +1,11 @@
type
Item* = ref object
nodeAddress: string
proc initItem*(nodeAddress: string): Item =
result = Item()
result.nodeAddress = nodeAddress
proc nodeAddress*(self: Item): string =
self.nodeAddress

View File

@ -0,0 +1,62 @@
import NimQml, Tables
import item
type
ModelRole {.pure.} = enum
NodeAddress = UserRole + 1
QtObject:
type Model* = ref object of QAbstractListModel
items*: seq[Item]
proc setup(self: Model) =
self.QAbstractListModel.setup
proc delete(self: Model) =
self.items = @[]
self.QAbstractListModel.delete
proc newModel*(): Model =
new(result, delete)
result.setup
method rowCount(self: Model, index: QModelIndex = nil): int =
return self.items.len
method roleNames(self: Model): Table[int, string] =
{
ModelRole.NodeAddress.int:"nodeAddress"
}.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
if index.row < 0 or index.row >= self.items.len:
return
let item = self.items[index.row]
let enumRole = role.ModelRole
case enumRole:
of ModelRole.NodeAddress:
result = newQVariant(item.nodeAddress)
proc addItem*(self: Model, item: Item) =
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
self.beginInsertRows(parentModelIndex, self.items.len, self.items.len)
self.items.add(item)
self.endInsertRows()
proc addItems*(self: Model, items: seq[Item]) =
if(items.len == 0):
return
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
let first = self.items.len
let last = first + items.len - 1
self.beginInsertRows(parentModelIndex, first, last)
self.items.add(items)
self.endInsertRows()

View File

@ -0,0 +1,66 @@
import NimQml, chronicles
import io_interface
import ../io_interface as delegate_interface
import view, controller, model, item
import ../../../../core/eventemitter
import ../../../../../app_service/service/settings/service as settings_service
import ../../../../../app_service/service/node_configuration/service as node_configuration_service
export io_interface
logScope:
topics = "profile-section-waku-module"
type
Module* = ref object of io_interface.AccessInterface
delegate: delegate_interface.AccessInterface
controller: Controller
view: View
viewVariant: QVariant
moduleLoaded: bool
proc newModule*(delegate: delegate_interface.AccessInterface,
events: EventEmitter,
settingsService: settings_service.Service,
nodeConfigurationService: node_configuration_service.Service): Module =
result = Module()
result.delegate = delegate
result.view = view.newView(result)
result.viewVariant = newQVariant(result.view)
result.controller = controller.newController(result, events, settingsService, nodeConfigurationService)
result.moduleLoaded = false
method delete*(self: Module) =
self.view.delete
self.viewVariant.delete
self.controller.delete
method load*(self: Module) =
self.controller.init()
self.view.load()
method isLoaded*(self: Module): bool =
return self.moduleLoaded
proc initModel(self: Module) =
var items: seq[Item]
let allWakuNodes = self.controller.getAllWakuNodes()
for w in allWakuNodes:
let item = initItem(w)
items.add(item)
self.view.model().addItems(items)
method viewDidLoad*(self: Module) =
self.initModel()
self.moduleLoaded = true
self.delegate.wakuModuleDidLoad()
method getModuleAsVariant*(self: Module): QVariant =
return self.viewVariant
method saveNewWakuNode*(self: Module, nodeAddress: string) =
self.controller.saveNewWakuNode(nodeAddress)
let item = initItem(nodeAddress)
self.view.model().addItem(item)

View File

@ -0,0 +1,39 @@
import NimQml
import io_interface, model
QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
activeMailserver: string
model: Model
modelVariant: QVariant
proc delete*(self: View) =
self.model.delete
self.modelVariant.delete
self.QObject.delete
proc newView*(delegate: io_interface.AccessInterface): View =
new(result, delete)
result.QObject.setup
result.delegate = delegate
result.activeMailserver = ""
result.model = newModel()
result.modelVariant = newQVariant(result.model)
proc load*(self: View) =
self.delegate.viewDidLoad()
proc model*(self: View): Model =
return self.model
proc modelChanged*(self: View) {.signal.}
proc getModel(self: View): QVariant {.slot.} =
return self.modelVariant
QtProperty[QVariant] model:
read = getModel
notify = modelChanged
proc saveNewWakuNode(self: View, address: string) {.slot.} =
self.delegate.saveNewWakuNode(address)

View File

@ -100,6 +100,9 @@ type
EnableDiscV5*: bool
UDPPort*: int
AutoUpdate*: bool
EnableStore*: bool
StoreCapacity*: int
StoreSeconds*: int
ShhextConfig* = object
PFSEnabled*: bool
@ -322,6 +325,9 @@ proc toWaku2Config*(jsonObj: JsonNode): Waku2Config =
discard jsonObj.getProp("EnableDiscV5", result.EnableDiscV5)
discard jsonObj.getProp("UDPPort", result.UDPPort)
discard jsonObj.getProp("AutoUpdate", result.AutoUpdate)
discard jsonObj.getProp("EnableStore", result.EnableStore)
discard jsonObj.getProp("StoreCapacity", result.StoreCapacity)
discard jsonObj.getProp("StoreSeconds", result.StoreSeconds)
proc toWakuConfig*(jsonObj: JsonNode): WakuConfig =
discard jsonObj.getProp("Enabled", result.Enabled)

View File

@ -33,6 +33,7 @@ type
configuration: NodeConfigDto
fleetConfiguration: FleetConfiguration
settingsService: settings_service.Service
wakuNodes: seq[string]
events: EventEmitter
# Forward declarations
@ -48,6 +49,7 @@ proc newService*(fleetConfiguration: FleetConfiguration, settingsService: settin
result.fleetConfiguration = fleetConfiguration
result.settingsService = settingsService
result.events = events
result.wakuNodes = @[]
proc adaptNodeSettingsForTheAppNeed(self: Service) =
self.configuration.DataDir = "./ethereum"
@ -68,6 +70,10 @@ proc init*(self: Service) =
let response = status_node_config.getNodeConfig()
self.configuration = response.result.toNodeConfigDto()
let wakuNodes = self.configuration.ClusterConfig.WakuNodes
for nodeAddress in wakuNodes:
self.wakuNodes.add(nodeAddress)
self.adaptNodeSettingsForTheAppNeed()
except Exception as e:
let errDesription = e.msg
@ -202,6 +208,15 @@ proc getFleet*(self: Service): Fleet =
proc getFleetAsString*(self: Service): string =
result = $self.getFleet()
proc getAllWakuNodes*(self: Service): seq[string] =
return self.wakuNodes
proc saveNewWakuNode*(self: Service, nodeAddress: string) =
var newConfiguration = self.configuration
newConfiguration.ClusterConfig.WakuNodes.add(nodeAddress)
self.configuration = newConfiguration
discard self.saveConfiguration(newConfiguration)
proc setFleet*(self: Service, fleet: string): bool =
if(not self.settingsService.saveFleet(fleet)):
error "error saving fleet ", procName="setFleet"
@ -270,3 +285,13 @@ proc isV2LightMode*(self: Service): bool =
proc isFullNode*(self: Service): bool =
return self.configuration.WakuConfig.FullNode
method isWakuV2StoreEnabled*(self: Service): bool =
return self.configuration.WakuV2Config.EnableStore
proc setWakuV2StoreEnabled*(self: Service, enabled: bool, storeCapacity: int = 0, storeSeconds: int = 0): bool =
var newConfiguration = self.configuration
newConfiguration.WakuV2Config.EnableStore = enabled
newConfiguration.WakuV2Config.StoreCapacity = storeCapacity
newConfiguration.WakuV2Config.StoreSeconds = storeSeconds
return self.saveConfiguration(newConfiguration)

View File

@ -145,6 +145,8 @@ QtObject {
property var walletSectionTransactionsInst: walletSectionTransactions
property bool isWakuV2StoreEnabled: advancedModule ? advancedModule.isWakuV2StoreEnabled : false
property string communityTags: communitiesModule.tags
property var stickersModuleInst: stickersModule

View File

@ -26,8 +26,7 @@ StatusModal {
}
onOpened: {
nameInput.text = "";
enodeInput.text = "";
addrInput.text = "";
}
contentItem: StatusScrollView {
@ -37,29 +36,19 @@ StatusModal {
Column {
id: nodesColumn
width: parent.width
StatusInput {
id: nameInput
label: qsTr("Name")
placeholderText: qsTr("Specify a name")
validators: [StatusMinLengthValidator {
minLength: 1
errorMessage: qsTr("You need to enter a name")
}]
validationMode: StatusInput.ValidationMode.Always
}
StatusInput {
id: enodeInput
label: popup.advancedStore.isWakuV2 ? qsTr("Storenode multiaddress") : qsTr("History node address")
placeholderText: popup.advancedStore.isWakuV2 ? "/ip4/0.0.0.0/tcp/123/..." : "enode://{enode-id}:{password}@{ip-address}:{port-number}"
id: addrInput
label: qsTr("Node multiaddress or DNS Discovery address")
placeholderText: "/ipv4/0.0.0.0/tcp/123/..."
validators: [
StatusMinLengthValidator {
minLength: 1
errorMessage: popup.advancedStore.isWakuV2 ? qsTr("You need to enter the storenode multiaddress") : qsTr("You need to enter the enode address")
errorMessage: qsTr("You need to enter a value")
},
StatusRegularExpressionValidator {
errorMessage: popup.advancedStore.isWakuV2 ? qsTr('Multiaddress must start with a "/"') : qsTr("The format must be: enode://{enode-id}:{password}@{ip-address}:{port}")
regularExpression: popup.advancedStore.isWakuV2 ? /\/.+/ : /enode:\/\/[a-z0-9]+:[a-z0-9]+@(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}:[0-9]+/
errorMessage: qsTr("Value should start with '/' or 'enr:'")
regularExpression: /(\/|enr:).+/
}]
validationMode: StatusInput.ValidationMode.Always
}
@ -69,10 +58,9 @@ StatusModal {
rightButtons: [
StatusButton {
text: qsTr("Save")
enabled: nameInput.valid && enodeInput.valid
// enabled: nameInput.text !== "" && enodeInput.text !== ""
enabled: addrInput.valid
onClicked: {
root.messagingStore.saveNewMailserver(nameInput.text, enodeInput.text)
root.messagingStore.saveNewWakuNode(addrInput.text)
popup.close()
}
}

View File

@ -0,0 +1,80 @@
import QtQuick 2.12
import QtQuick.Controls 2.3
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
import StatusQ.Popups 0.1
import utils 1.0
StatusModal {
id: popup
anchors.centerIn: parent
height: 560
header.title: qsTr("Waku nodes")
property var messagingStore
property var advancedStore
onClosed: {
destroy()
}
onOpened: {
nameInput.text = "";
enodeInput.text = "";
}
contentItem: StatusScrollView {
height: parent.height
width: parent.width
Column {
id: nodesColumn
width: parent.width
StatusInput {
id: nameInput
label: qsTr("Name")
placeholderText: qsTr("Specify a name")
validators: [StatusMinLengthValidator {
minLength: 1
errorMessage: qsTr("You need to enter a name")
}]
validationMode: StatusInput.ValidationMode.Always
}
StatusInput {
id: enodeInput
label: popup.advancedStore.isWakuV2 ? qsTr("Storenode multiaddress") : qsTr("History node address")
placeholderText: popup.advancedStore.isWakuV2 ? "/ip4/0.0.0.0/tcp/123/..." : "enode://{enode-id}:{password}@{ip-address}:{port-number}"
validators: [
StatusMinLengthValidator {
minLength: 1
errorMessage: popup.advancedStore.isWakuV2 ? qsTr("You need to enter the storenode multiaddress") : qsTr("You need to enter the enode address")
},
StatusRegularExpressionValidator {
errorMessage: popup.advancedStore.isWakuV2 ? qsTr('Multiaddress must start with a "/"') : qsTr("The format must be: enode://{enode-id}:{password}@{ip-address}:{port}")
regularExpression: popup.advancedStore.isWakuV2 ? /\/.+/ : /enode:\/\/[a-z0-9]+:[a-z0-9]+@(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}:[0-9]+/
}]
validationMode: StatusInput.ValidationMode.Always
}
}
}
rightButtons: [
StatusButton {
text: qsTr("Save")
enabled: nameInput.valid && enodeInput.valid
// enabled: nameInput.text !== "" && enodeInput.text !== ""
onClicked: {
root.messagingStore.saveNewMailserver(nameInput.text, enodeInput.text)
popup.close()
}
}
]
}

View File

@ -26,9 +26,7 @@ StatusModal {
property var messagingStore
property var advancedStore
property string nameValidationError: ""
property string enodeValidationError: ""
onClosed: {
destroy()
}
@ -41,74 +39,16 @@ StatusModal {
id: nodesColumn
width: parent.width
StatusListItem {
width: parent.width
title: qsTr("Use Waku nodes")
components: [
StatusSwitch {
checked: root.messagingStore.useMailservers
onCheckedChanged: root.messagingStore.toggleUseMailservers(checked)
}
]
onClicked: {
root.messagingStore.toggleUseMailservers(!root.messagingStore.useMailservers)
}
}
Separator {
width: parent.width
}
StatusListItem {
width: parent.width
title: qsTr("Select node automatically")
components: [
StatusSwitch {
id: automaticSelectionSwitch
checked: root.messagingStore.automaticMailserverSelection
onCheckedChanged: root.messagingStore.enableAutomaticMailserverSelection(checked)
}
]
onClicked: {
automaticSelectionSwitch.checked = !automaticSelectionSwitch.checked
}
}
StatusSectionHeadline {
text: qsTr("Waku Nodes")
visible: !automaticSelectionSwitch.checked
width: parent.width
height: visible ? implicitHeight : 0
}
ButtonGroup {
id: nodesButtonGroup
}
Repeater {
id: mailServersListView
model: root.messagingStore.mailservers
id: wakunodesListView
model: root.messagingStore.wakunodes
delegate: Component {
StatusListItem {
title: qsTr("Node %1").arg(index + 1)
subTitle: model.name
visible: !automaticSelectionSwitch.checked
height: visible ? implicitHeight : 0
subTitle: model.nodeAddress
components: [
StatusRadioButton {
id: nodeRadioBtn
ButtonGroup.group: nodesButtonGroup
checked: model.nodeAddress === root.messagingStore.activeMailserver
onCheckedChanged: {
if (checked) {
root.messagingStore.setActiveMailserver(model.name)
}
}
}
// TODO: add a button to delete nodes and restore default fleet nodes if necessary
]
onClicked: {
nodeRadioBtn.checked = true
}
}
}
}

View File

@ -0,0 +1,136 @@
import QtQuick 2.12
import QtQuick.Controls 2.3
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
import StatusQ.Popups 0.1
import "."
import utils 1.0
import shared 1.0
import shared.panels 1.0
import shared.popups 1.0
import shared.status 1.0
import shared.controls 1.0
StatusModal {
id: root
anchors.centerIn: parent
height: 560
header.title: qsTr("History Nodes")
property var messagingStore
property var advancedStore
property string nameValidationError: ""
property string enodeValidationError: ""
onClosed: {
destroy()
}
contentItem: StatusScrollView {
height: parent.height
width: parent.width
Column {
id: nodesColumn
width: parent.width
StatusListItem {
width: parent.width
title: qsTr("Use Waku nodes")
components: [
StatusSwitch {
checked: root.messagingStore.useMailservers
onCheckedChanged: root.messagingStore.toggleUseMailservers(checked)
}
]
onClicked: {
root.messagingStore.toggleUseMailservers(!root.messagingStore.useMailservers)
}
}
Separator {
width: parent.width
}
StatusListItem {
width: parent.width
title: qsTr("Select node automatically")
components: [
StatusSwitch {
id: automaticSelectionSwitch
checked: root.messagingStore.automaticMailserverSelection
onCheckedChanged: root.messagingStore.enableAutomaticMailserverSelection(checked)
}
]
onClicked: {
automaticSelectionSwitch.checked = !automaticSelectionSwitch.checked
}
}
StatusSectionHeadline {
text: qsTr("Waku Nodes")
visible: !automaticSelectionSwitch.checked
width: parent.width
height: visible ? implicitHeight : 0
}
ButtonGroup {
id: nodesButtonGroup
}
Repeater {
id: mailServersListView
model: root.messagingStore.mailservers
delegate: Component {
StatusListItem {
title: qsTr("Node %1").arg(index + 1)
subTitle: model.name
visible: !automaticSelectionSwitch.checked
height: visible ? implicitHeight : 0
components: [
StatusRadioButton {
id: nodeRadioBtn
ButtonGroup.group: nodesButtonGroup
checked: model.nodeAddress === root.messagingStore.activeMailserver
onCheckedChanged: {
if (checked) {
root.messagingStore.setActiveMailserver(model.name)
}
}
}
]
onClicked: {
nodeRadioBtn.checked = true
}
}
}
}
StatusBaseText {
text: qsTr("Add a new node")
color: Theme.palette.primaryColor1
width: parent.width
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: Global.openPopup(wakuNodeModalComponent)
}
}
}
}
Component {
id: wakuNodeModalComponent
AddWakuNodeModal {
messagingStore: root.messagingStore
advancedStore: root.advancedStore
}
}
}

View File

@ -13,6 +13,8 @@ QtObject {
property bool isTelemetryEnabled: advancedModule? advancedModule.isTelemetryEnabled : false
property bool isAutoMessageEnabled: advancedModule? advancedModule.isAutoMessageEnabled : false
property bool isDebugEnabled: advancedModule? advancedModule.isDebugEnabled : false
property bool isCommunityHistoryArchiveSupportEnabled: advancedModule? advancedModule.isCommunityHistoryArchiveSupportEnabled : false
property bool isWakuV2StoreEnabled: advancedModule ? advancedModule.isWakuV2StoreEnabled : false
property var customNetworksModel: advancedModule? advancedModule.customNetworksModel : []
@ -32,6 +34,7 @@ QtObject {
readonly property string communitiesPortal: "communitiesPortal"
readonly property string communityPermissions: "communityPermissions"
readonly property string discordImportTool: "discordImportTool"
readonly property string wakuV2StoreEnabled: "wakuV2StoreEnabled"
}
function logDir() {
@ -113,6 +116,10 @@ QtObject {
else if (feature === experimentalFeatures.communitiesPortal) {
advancedModule.toggleCommunitiesPortalSection()
}
else if (feature === experimentalFeatures.wakuV2StoreEnabled) {
// toggle history archive support
advancedModule.toggleWakuV2Store()
}
else if (feature === experimentalFeatures.activityCenter) {
localAccountSensitiveSettings.isActivityCenterEnabled = !localAccountSensitiveSettings.isActivityCenterEnabled
}

View File

@ -6,8 +6,10 @@ QtObject {
property var privacyModule
property var syncModule
property var wakuModule
property var mailservers: syncModule.model
property var wakunodes: wakuModule.model
property bool useMailservers: syncModule.useMailservers
@ -31,6 +33,10 @@ QtObject {
root.syncModule.saveNewMailserver(name, nodeAddress)
}
function saveNewWakuNode(nodeAddress) {
root.wakuModule.saveNewWakuNode(nodeAddress)
}
function enableAutomaticMailserverSelection(checked) {
if (automaticMailserverSelection === checked) {
return

View File

@ -25,6 +25,7 @@ QtObject {
property MessagingStore messagingStore: MessagingStore {
privacyModule: profileSectionModuleInst.privacyModule
syncModule: profileSectionModuleInst.syncModule
wakuModule: profileSectionModuleInst.wakuModule
}
property DevicesStore devicesStore: DevicesStore {

View File

@ -185,6 +185,19 @@ SettingsContentBase {
}
}
// TODO: replace with StatusQ component
StatusSettingsLineButton {
anchors.leftMargin: 0
anchors.rightMargin: 0
text: qsTr("WakuV2 Store")
isSwitch: true
visible: root.advancedStore.isWakuV2
switchChecked: root.advancedStore.isWakuV2StoreEnabled
onClicked: {
Global.openPopup(enableWakuV2StoreComponent)
}
}
StatusSectionHeadline {
anchors.left: parent.left
anchors.right: parent.right
@ -278,7 +291,7 @@ SettingsContentBase {
anchors.right: parent.right
anchors.leftMargin: Style.current.padding
anchors.rightMargin: Style.current.padding
visible: root.advancedStore.isWakuV2
visible: root.advancedStore.isWakuV2 && root.advancedStore.fleet != Constants.status_prod
text: qsTr("WakuV2 mode")
topPadding: Style.current.bigPadding
bottomPadding: Style.current.padding
@ -487,6 +500,27 @@ SettingsContentBase {
}
}
Component {
id: enableWakuV2StoreComponent
ConfirmationDialog {
property bool mode: false
id: confirmDialog
showCancelButton: true
confirmationText: qsTr("Are you sure you want to %1 WakuV2 Store? You need to restart the app for this change to take effect.")
.arg(root.advancedStore.isWakuV2StoreEnabled ?
qsTr("disable") :
qsTr("enable"))
onConfirmButtonClicked: {
root.advancedStore.toggleExperimentalFeature(root.advancedStore.experimentalFeatures.wakuV2StoreEnabled)
close()
}
onCancelButtonClicked: {
close()
}
}
}
Component {
id: enableDebugComponent
ConfirmationDialog {

View File

@ -337,7 +337,7 @@ SettingsContentBase {
StatusListItem {
Layout.fillWidth: true
title: qsTr("Waku nodes")
title: qsTr("History nodes")
label: root.messagingStore.getMailserverNameForNodeAddress(root.messagingStore.activeMailserver)
components: [
StatusIcon {
@ -346,11 +346,33 @@ SettingsContentBase {
color: Theme.palette.baseColor1
}
]
onClicked: Global.openPopup(wakuNodeModalComponent)
onClicked: Global.openPopup(wakuStoreModalComponent)
}
Component {
id: wakuNodeModalComponent
id: wakuStoreModalComponent
WakuStoreModal {
messagingStore: root.messagingStore
advancedStore: root.advancedStore
}
}
StatusListItem {
Layout.fillWidth: true
title: qsTr("Waku Nodes")
visible: root.advancedStore.isWakuV2
components: [
StatusIcon {
icon: "chevron-down"
rotation: 270
color: Theme.palette.baseColor1
}
]
onClicked: Global.openPopup(wakuNodesModalComponent)
}
Component {
id: wakuNodesModalComponent
WakuNodesModal {
messagingStore: root.messagingStore
advancedStore: root.advancedStore