From bfb431cbafc8683239c0bdd8535481e900bfff89 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Mon, 26 Oct 2020 13:29:58 -0700 Subject: [PATCH] Experimental Gating + Server List Sketch --- go.mod | 4 ++ go.sum | 10 +++++ go/constants/server_manager_events.go | 8 ++++ go/servers/server_manager.go | 57 ++++++++++++++++++++++++ go/ui/gcd.go | 37 ++++++++++++++-- go/ui/settings.go | 13 ++++-- main.go | 4 +- qml/main.qml | 12 ++++- qml/panes/ProfileManagerPane.qml | 3 ++ qml/panes/ServerAddEditPane.qml | 63 +++++++++++++++++++++++++++ qml/panes/SettingsPane.qml | 63 ++++++++++++++------------- qml/utils.js | 16 +++++++ qml/widgets/ExperimentToggle.qml | 24 ++++++++++ qml/widgets/ServerList.qml | 51 ++++++++++------------ qml/widgets/ServerRow.qml | 60 +++++++++++++++++++++++++ 15 files changed, 357 insertions(+), 68 deletions(-) create mode 100644 go/constants/server_manager_events.go create mode 100644 go/servers/server_manager.go create mode 100644 qml/panes/ServerAddEditPane.qml create mode 100644 qml/widgets/ExperimentToggle.qml create mode 100644 qml/widgets/ServerRow.qml diff --git a/go.mod b/go.mod index 72323dbf..dee60d83 100644 --- a/go.mod +++ b/go.mod @@ -16,4 +16,8 @@ require ( github.com/therecipe/qt v0.0.0-20200126204426-5074eb6d8c41 github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20200126204426-5074eb6d8c41 // indirect github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200126204426-5074eb6d8c41 // indirect + go.etcd.io/bbolt v1.3.5 // indirect + golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect + golang.org/x/net v0.0.0-20201022231255-08b38378de70 // indirect + golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd // indirect ) diff --git a/go.sum b/go.sum index 28d0bdd0..0df9051c 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +cwtch.im v0.4.3 h1:3c//RcO0+YFqm0XOILq7ScsGgQsA4waLU1XAbQRH3n4= cwtch.im/cwtch v0.4.2-0.20201008200820-a2c5a28e092d h1:CuqoPJdfmKqvGnZhQtrv/9YqTRei3t06AvCGrCmD3gU= cwtch.im/cwtch v0.4.2-0.20201008200820-a2c5a28e092d/go.mod h1:EvZQDbvXNu38m785dWF0MMljqJzwWrNTFT40HvoEAhI= cwtch.im/cwtch v0.4.2-0.20201016053957-1933fb703fb0 h1:8d2hJyb6qupb9wS6px3734Hy1aHOrtwk4fpM1z/o3Tg= @@ -60,6 +61,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/struCoder/pidusage v0.1.3 h1:pZcSa6asBE38TJtW0Nui6GeCjLTpaT/jAnNP7dUTLSQ= github.com/struCoder/pidusage v0.1.3/go.mod h1:pWBlW3YuSwRl6h7R5KbvA4N8oOqe9LjaKW5CwT1SPjI= github.com/therecipe/qt v0.0.0-20200126204426-5074eb6d8c41 h1:yBVcrpbaQYJBdKT2pxTdlL4hBE/eM4UPcyj9YpyvSok= github.com/therecipe/qt v0.0.0-20200126204426-5074eb6d8c41/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us= @@ -70,6 +72,8 @@ github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200126204426 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -80,6 +84,8 @@ golang.org/x/crypto v0.0.0-20200420104511-884d27f42877/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= @@ -93,6 +99,8 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb h1:mUVeFHoDKis5nxCAzoAi7E8Ghb86EXh/RK6wtvJIqRY= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201022231255-08b38378de70 h1:Z6x4N9mAi4oF0TbHweCsH618MO6OI6UFgV0FP5n0wBY= +golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -111,6 +119,8 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ix golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd h1:WgqgiQvkiZWz7XLhphjt2GI2GcGCTIZs9jqXMWmH+oc= +golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/go/constants/server_manager_events.go b/go/constants/server_manager_events.go new file mode 100644 index 00000000..d10e0954 --- /dev/null +++ b/go/constants/server_manager_events.go @@ -0,0 +1,8 @@ +package constants + +import "cwtch.im/cwtch/event" + +// The server manage defines its own events... +const ( + ListServers = event.Type("ListServers") +) \ No newline at end of file diff --git a/go/servers/server_manager.go b/go/servers/server_manager.go new file mode 100644 index 00000000..27ada919 --- /dev/null +++ b/go/servers/server_manager.go @@ -0,0 +1,57 @@ +package servers + +import ( + "cwtch.im/cwtch/event" + "cwtch.im/cwtch/server" + "cwtch.im/ui/go/constants" + "cwtch.im/ui/go/the" + "cwtch.im/ui/go/ui" + "git.openprivacy.ca/openprivacy/log" +) + + + +// ServerManager is responsible for managing user operated servers +type ServerManager struct { + servers map[string]server.Server +} + +func LaunchServiceManager(gcd *ui.GrandCentralDispatcher) { + sm := new(ServerManager) + sm.Init(gcd) +} + + +func (sm * ServerManager) Init(gcd *ui.GrandCentralDispatcher) { + sm.servers = make(map[string]server.Server) + + q := event.NewQueue() + the.AppBus.Subscribe(constants.ListServers, q) + + for { + e := q.Next() + + switch e.EventType { + case constants.ListServers: { + sm.ListServers(gcd) + } + } + } +} + +// TODO Replace with details from actual hosted servers. Right now these values are used to sketch / test out the +// UI QML +func (sm * ServerManager) ListServers(gcd *ui.GrandCentralDispatcher) { + log.Debugf("Listing Servers...") + gcd.AddServer("Server 1","Server 1","server",0) + gcd.AddServer("Server 2","Server 2","server",4) + gcd.AddServer("Server 3","Server 3","server",4) +} + +func (sm * ServerManager) StartServer(handle string) { + // TODO Start the server with the given handle config +} + +func (sm * ServerManager) StopServer(handle string) { + // TODO Stop the given server +} \ No newline at end of file diff --git a/go/ui/gcd.go b/go/ui/gcd.go index 4f4984c5..9ff82a0d 100644 --- a/go/ui/gcd.go +++ b/go/ui/gcd.go @@ -1,17 +1,16 @@ package ui import ( - "encoding/base64" - "strconv" - "sync" - "cwtch.im/cwtch/app" "cwtch.im/cwtch/event" "cwtch.im/cwtch/model" "cwtch.im/cwtch/model/attr" "cwtch.im/cwtch/protocol/connections" "cwtch.im/ui/go/constants" + "encoding/base64" "github.com/therecipe/qt/qml" + "strconv" + "sync" "cwtch.im/ui/go/the" "encoding/base32" @@ -46,6 +45,8 @@ type GrandCentralDispatcher struct { _ string `property:"assetPath"` _ string `property:"selectedProfile,auto"` _ string `property:"selectedConversation,auto"` + _ bool `property:experimentsEnabled,auto,changed` + _ map[string]bool `property:experiments,auto,changed` // profile management stuff _ func() `signal:"Loaded"` @@ -55,6 +56,10 @@ type GrandCentralDispatcher struct { _ func() `signal:"ResetProfileList"` _ func(failed bool) `signal:"ChangePasswordResponse"` + // server management + _ func(handle, displayname, image string, status int) `signal:"AddServer"` + _ func() `signal:"requestServers,auto"` + // contact list stuff _ func(handle, displayName, image string, badge, status int, authorization string, loading bool, lastMsgTime int) `signal:"AddContact"` _ func(handle, displayName string) `signal:"UpdateContactDisplayName"` @@ -130,6 +135,8 @@ func (this *GrandCentralDispatcher) init() { this.GlobalSettings = ReadGlobalSettings() this.SetThemeScale(this.GlobalSettings.Zoom) this.SetTheme(this.GlobalSettings.Theme) + this.SetExperimentsEnabled(this.GlobalSettings.ExperimentsEnabled) + this.SetExperiments(this.GlobalSettings.Experiments) } // GetUiManager gets (and creates if required) a ui Manager for the supplied profile id @@ -377,6 +384,12 @@ func (this *GrandCentralDispatcher) requestServerSettings(groupID string) { this.SupplyServerSettings(group.GroupServer, keyNames, keys) } +func (this *GrandCentralDispatcher) requestServers() { + the.AppBus.Publish(event.NewEvent(constants.ListServers, map[event.Field]string{ + + })) +} + func (this *GrandCentralDispatcher) requestGroupSettings(groupID string) { group := the.Peer.GetGroup(groupID) @@ -631,6 +644,22 @@ func (this *GrandCentralDispatcher) themeScaleChanged(newThemeScale float32) { WriteGlobalSettings(this.GlobalSettings) } +// Turn on/off global experiments +func (this *GrandCentralDispatcher) experimentsEnabledChanged(enabled bool) { + this.GlobalSettings.ExperimentsEnabled = enabled + log.Debugf("Experiments Enabled: %v %v", enabled, this.GlobalSettings.ExperimentsEnabled) + WriteGlobalSettings(this.GlobalSettings) +} + +// Turn on/off global experiments +func (this *GrandCentralDispatcher) experimentsChanged(experiments map[string]bool) { + for k, v := range experiments { + this.GlobalSettings.Experiments[k] = v + } + log.Debugf("Experiments: %v", experiments) + WriteGlobalSettings(this.GlobalSettings) +} + func (this *GrandCentralDispatcher) themeChanged(newTheme string) { this.GlobalSettings.Theme = newTheme WriteGlobalSettings(this.GlobalSettings) diff --git a/go/ui/settings.go b/go/ui/settings.go index 7cbb3aca..a5d40edf 100644 --- a/go/ui/settings.go +++ b/go/ui/settings.go @@ -16,10 +16,12 @@ const GlobalSettingsFilename = "ui.globals" const saltFile = "SALT" type GlobalSettings struct { - Zoom float32 - Locale string - Theme string - PreviousPid int64 + Zoom float32 + Locale string + Theme string + PreviousPid int64 + ExperimentsEnabled bool + Experiments map[string]bool } var DefaultGlobalSettings = GlobalSettings{ @@ -27,6 +29,8 @@ var DefaultGlobalSettings = GlobalSettings{ Locale: "en", Theme: "light", PreviousPid: -1, + ExperimentsEnabled: false, + Experiments: make(map[string]bool), } func InitGlobalSettingsFile(directory string, password string) error { @@ -68,6 +72,7 @@ func ReadGlobalSettings() *GlobalSettings { if err != nil { log.Errorf("Could not parse global ui settings: %v\n", err) } + log.Debugf("MAP: %v", settings.Experiments) return &settings } diff --git a/main.go b/main.go index 5ecf927c..40aae889 100644 --- a/main.go +++ b/main.go @@ -4,10 +4,11 @@ import ( "crypto/rand" libapp "cwtch.im/cwtch/app" "cwtch.im/cwtch/event" - "cwtch.im/cwtch/peer" "cwtch.im/cwtch/event/bridge" + "cwtch.im/cwtch/peer" "cwtch.im/ui/go/handlers" os2 "cwtch.im/ui/go/os" + "cwtch.im/ui/go/servers" "cwtch.im/ui/go/the" "cwtch.im/ui/go/ui" "cwtch.im/ui/go/ui/android" @@ -319,6 +320,7 @@ func loadNetworkingAndFiles(gcd *ui.GrandCentralDispatcher, service bool, client the.AppBus = the.CwtchApp.GetPrimaryBus() subscribed := make(chan bool) go handlers.App(gcd, subscribed, clientUI) + go servers.LaunchServiceManager(gcd) <-subscribed } diff --git a/qml/main.qml b/qml/main.qml index 8d3c3b30..d81ea688 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -111,6 +111,7 @@ ApplicationWindow { readonly property int settingsPane: 2 readonly property int addEditProfilePane: 3 readonly property int profilePane: 4 + readonly property int addEditServerPane: 5 property alias pane: parentStack.currentIndex Rectangle { // Splash pane @@ -164,7 +165,6 @@ ApplicationWindow { } } - RowLayout { // Profile Pane (contact list + overlays) Layout.fillHeight: true Layout.fillWidth: true @@ -251,8 +251,18 @@ ApplicationWindow { } } + Rectangle { // Server Add / Edit pane + Layout.fillHeight: true + Layout.fillWidth: true + color: Theme.backgroundPaneColor + ServerAddEditPane{ + id: serverAddEditPane + anchors.fill: parent + } + } + focus: true Keys.onPressed: { if (event.key == Qt.Key_Back) { diff --git a/qml/panes/ProfileManagerPane.qml b/qml/panes/ProfileManagerPane.qml index 903a8313..0cea77ce 100644 --- a/qml/panes/ProfileManagerPane.qml +++ b/qml/panes/ProfileManagerPane.qml @@ -16,6 +16,7 @@ import "../opaque/styles" import "../opaque/theme" import "../opaque/fonts" +import "../utils.js" as Utils ColumnLayout { id: thecol @@ -127,6 +128,8 @@ ColumnLayout { } Rectangle { + // TODO Remove Experiment Check once Feature is Stable + visible: gcd.experimentsEnabled && Utils.checkMap(gcd.experiments, "tapir-servers-experiment") color: Theme.backgroundMainColor Layout.fillHeight: true diff --git a/qml/panes/ServerAddEditPane.qml b/qml/panes/ServerAddEditPane.qml new file mode 100644 index 00000000..342073ed --- /dev/null +++ b/qml/panes/ServerAddEditPane.qml @@ -0,0 +1,63 @@ +import QtGraphicalEffects 1.0 +import QtQuick 2.7 +import QtQuick.Controls 2.13 +import QtQuick.Controls.Material 2.0 +import QtQuick.Layouts 1.3 +import QtQuick.Window 2.11 + + +import "../opaque" as Opaque +import "../opaque/theme" +// import "../styles" + + + +Opaque.SettingsList { // Add Profile Pane + id: serverAddEditPane + anchors.fill: parent + + function reset() { + serverAddEditPane.server_name = ""; + serverAddEditPane.server_available = false; + } + + property string server_name; + property bool server_available; + + function load(server_onion, server_name, server_available) { + reset(); + serverAddEditPane.server_name = server_name; + serverAddEditPane.server_available = server_available; + } + + settings: Column { + anchors.horizontalCenter: parent.horizontalCenter + width: 700 + + Opaque.ScalingLabel { + text: server_name + size: 48 + } + + Opaque.Setting { + label: qsTr("server-availability") + + + field: Opaque.ToggleSwitch { + anchors.right: parent.right + + isToggled: serverAddEditPane.server_available + onToggled: function() { + serverAddEditPane.serverAddEditPane = !serverAddEditPane + } + } + } + } + + + Connections { // UPDATE UNREAD MESSAGES COUNTER + target: gcd + + + } +} diff --git a/qml/panes/SettingsPane.qml b/qml/panes/SettingsPane.qml index 48eb2061..e10d2dec 100644 --- a/qml/panes/SettingsPane.qml +++ b/qml/panes/SettingsPane.qml @@ -10,6 +10,8 @@ import QtQuick.Controls.Styles 1.4 import "../opaque" as Opaque import "../opaque/controls" import "../opaque/theme" +import "../widgets" as Widgets +import "../utils.js" as Utils Opaque.SettingsList { // settingsPane id: root @@ -118,6 +120,37 @@ Opaque.SettingsList { // settingsPane } } + // Experimental Gating + + Opaque.Setting { + //: Theme + label: qsTr("experiments-enabled") + + field: Opaque.ToggleSwitch { + anchors.right: parent.right + id: experimentsEnabledToggle + isToggled: gcd.experimentsEnabled + onToggled: function() { + console.log("experiments enabled: " + gcd.experimentsEnabled + " " + experimentsEnabledToggle.isToggled) ; + if (gcd.experimentsEnabled == false) { + gcd.experimentsEnabled = true; + } else { + gcd.experimentsEnabled = false; + } + } + } + } + + Widgets.ExperimentToggle { + name: "servers_enabled" + experiment_id: "tapir-servers-experiment" + } + + Widgets.ExperimentToggle { + name: "groups_enabled" + experiment_id: "tapir-groups-experiment" + } + } @@ -168,33 +201,3 @@ Opaque.SettingsList { // settingsPane //end of flickable } - -/* Opaque.ScalingLabel { -width: parent.width -wrapMode: TextEdit.Wrap -//: Interface zoom (mostly affects text and button sizes) -text: qsTr("zoom-label") + ":" -} - - - - -CheckBox { -id: blockUnknownToggle -checked: true -onClicked: { -if (blockUnknownToggle.checked) { -gcd.blockUnknownPeers() -} else { -gcd.allowUnknownPeers() -} -} -style: CheckBoxStyle { -label: Opaque.ScalingLabel { -text: qsTr("block-unknown-label") -} -} -} -*/ - - diff --git a/qml/utils.js b/qml/utils.js index 63fe4f4d..175f07a1 100644 --- a/qml/utils.js +++ b/qml/utils.js @@ -46,6 +46,22 @@ function isGridOccupied(x, y, points) { return inPoints } +function checkMap(obj, key) { + let map = buildMap(obj); + if (map[key] != undefined) { + return map[key]; + } + return false +} + +function buildMap(obj) { + let map = new Map(); + Object.keys(obj).forEach(key => { + map[key] = obj[key]; + }); + return map; +} + function isGroup(id) { return id.length == 32 } diff --git a/qml/widgets/ExperimentToggle.qml b/qml/widgets/ExperimentToggle.qml new file mode 100644 index 00000000..12d71f23 --- /dev/null +++ b/qml/widgets/ExperimentToggle.qml @@ -0,0 +1,24 @@ +import "../opaque" as Opaque +import "../opaque/controls" +import "../opaque/theme" +import "../utils.js" as Utils + +Opaque.Setting { + //: Theme + id: experiment + property string name; + property string experiment_id; + visible: gcd.experimentsEnabled + label: qsTr(name) + + field: Opaque.ToggleSwitch { + anchors.right: parent.right + id: expToggle + isToggled: Utils.checkMap(gcd.experiments, experiment.experiment_id) + onToggled: function() { + let experimentsMap = Utils.buildMap(gcd.experiments); + experimentsMap[experiment.experiment_id] = expToggle.isToggled ? false : true; + gcd.experiments = experimentsMap; + } + } + } \ No newline at end of file diff --git a/qml/widgets/ServerList.qml b/qml/widgets/ServerList.qml index 5d50ea44..5c596be4 100644 --- a/qml/widgets/ServerList.qml +++ b/qml/widgets/ServerList.qml @@ -36,62 +36,54 @@ ColumnLayout { Connections { // ADD/REMOVE CONTACT ENTRIES target: gcd - onAddProfile: function(handle, displayName, image, tag) { + onLoaded: function() { + gcd.requestServers(); + } + + onAddServer: function(handle, displayName, image, status) { // don't add duplicates - for (var i = 0; i < profilesModel.count; i++) { - if (profilesModel.get(i)["_handle"] == handle) { + for (var i = 0; i < serversModel.count; i++) { + if (serversModel.get(i)["_handle"] == handle) { return } } // find index for insert (sort by onion) - var index = profilesModel.count - for (var i = 0; i < profilesModel.count; i++) { - if (profilesModel.get(i)["_handle"] > handle) { + var index = serversModel.count + for (var i = 0; i < serversModel.count; i++) { + if (serversModel.get(i)["_handle"] > handle) { index = i break } } - profilesModel.insert(index, + serversModel.insert(index, { _handle: handle, _displayName: displayName, _image: image, - _tag: tag, - _status: 4, + _status: status, }) } - /* - onRemoveProfile: function(handle) { - for(var i = 0; i < profilesModel.count; i++){ - if(profilesModel.get(i)["_handle"] == handle) { - console.log("deleting contact " + profilesModel.get(i)["_handle"]) - profilesModel.remove(i) - return - } - } - }*/ - - onResetProfileList: function() { - profilesModel.clear() + onResetServerList: function() { + serversModel.clear() } } ListModel { // Profile OBJECTS ARE STORED HERE ... - id: profilesModel + id: serversModel } Repeater { - id: profileList - model: profilesModel // ... AND DISPLAYED HERE - delegate: ProfileRow { + id: serverList + model: serversModel // ... AND DISPLAYED HERE + delegate: ServerRow { handle: _handle displayName: _displayName image: _image - tag: _tag + status: _status Layout.fillWidth: true } } @@ -113,8 +105,11 @@ ColumnLayout { } badgeColor: Theme.defaultButtonColor - onClicked: function(handle) { profileAddEditPane.reset(); parentStack.pane = parentStack.addEditProfilePane } + onClicked: function(handle) { serverAddEditPane.reset(); parentStack.pane = parentStack.addEditServerPane } } } } + + + } diff --git a/qml/widgets/ServerRow.qml b/qml/widgets/ServerRow.qml new file mode 100644 index 00000000..5cbc3bea --- /dev/null +++ b/qml/widgets/ServerRow.qml @@ -0,0 +1,60 @@ +import QtGraphicalEffects 1.0 +import QtQuick 2.7 +import QtQuick.Controls 2.4 +import QtQuick.Controls.Material 2.0 +import QtQuick.Layouts 1.3 +import CustomQmlTypes 1.0 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import "../opaque" as Opaque +import "../opaque/styles" +import "../opaque/theme" + +Opaque.PortraitRow { + id: root + property int status; + + portraitBorderColor: Theme.portraitOnlineBorderColor + portraitColor: Theme.portraitOnlineBackgroundColor + nameColor: Theme.portraitOnlineTextColor + onionColor: Theme.portraitOnlineTextColor + + badgeColor: status == 4 ? Theme.portraitOnlineBadgeColor : Theme.portraitOfflineBadgeColor + badgeVisible: true + + Opaque.Icon {// Edit BUTTON + id: btnEdit + source: gcd.assetPath + "core/edit-24px.svg" + + backgroundColor: root.color + iconColor: Theme.altTextColor + + anchors.right: parent.right + + //rectUnread.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 1 * gcd.themeScale + anchors.rightMargin: 20 * gcd.themeScale + + height: parent.height * 0.5 + width: parent.height * 0.5 + size: parent.height * 0.5 + + onClicked: { + serverAddEditPane.load(handle, displayName, status) + parentStack.pane = parentStack.addEditServerPane + } + + onHover: function (hover) { + root.isHover = hover + } + + } + + onClicked: function openClick(handle) { + + } + + +}