summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkali kaneko (leap communications) <kali@leap.se>2021-09-27 19:31:51 +0200
committerkali kaneko (leap communications) <kali@leap.se>2021-10-06 18:38:22 +0200
commit45a5f7f5a431f17d0fabb5fd08229936e4390598 (patch)
tree6a3c1818b64d606675688edf1522017ed2a84188
parent05730b9acb708bb311b7116ecc60d5e0c90e2ceb (diff)
[ui] pseudo-load based on distance
-rw-r--r--gui/components/Locations.qml183
-rw-r--r--gui/components/Preferences.qml4
-rw-r--r--gui/main.qml49
-rw-r--r--pkg/vpn/bonafide/gateways.go8
4 files changed, 136 insertions, 108 deletions
diff --git a/gui/components/Locations.qml b/gui/components/Locations.qml
index a0e7a30..ce510c5 100644
--- a/gui/components/Locations.qml
+++ b/gui/components/Locations.qml
@@ -5,10 +5,8 @@ import QtGraphicalEffects 1.0
import "../themes/themes.js" as Theme
+
/* TODO
- [ ] build a better gateway object list (location, user-friendly, country, bridge, etc)
- [ ] ui: mark manual override icon somehow?
- [ ] ui: auto radiobutton should use also the bridge icon
[ ] corner case: manual override, not full list yet
[ ] persist bridges
[ ] persist manual selection
@@ -17,13 +15,12 @@ import "../themes/themes.js" as Theme
(I think the backend should discard any manual selection when selecting bridges...
unless the current selection provides the bridge, in which case we can maintain it)
*/
-
ThemedPage {
id: locationPage
title: qsTr("Select Location")
- // TODO add ScrollIndicator
+ // TODO add ScrollIndicator
// https://doc.qt.io/qt-5.12//qml-qtquick-controls2-scrollindicator.html
//: this is in the radio button for the auto selection
@@ -68,7 +65,7 @@ ThemedPage {
WrappedRadioButton {
id: autoRadioButton
anchors.top: recommendedLabel.bottom
- text: getAutoLabel()
+ text: getAutoLabel()
ButtonGroup.group: locsel
checked: false
onClicked: {
@@ -84,85 +81,109 @@ ThemedPage {
id: manualBox
visible: root.locationsModel.length > 0
width: root.width * 0.90
- height: getManualBoxHeight()
radius: 10
color: Theme.fgColor
+ height: root.height * 0.60
+
anchors {
horizontalCenter: parent.horizontalCenter
top: autoBox.bottom
margins: 10
}
- Rectangle {
- anchors {
- fill: parent
- margins: 10
- }
- Label {
- id: manualLabel
- text: manualSelectionLabel
- font.bold: true
- }
- Label {
- id: bridgeWarning
- text: onlyBridgesWarning
- color: "gray"
- visible: isBridgeSelected()
- wrapMode: Text.Wrap
- anchors {
- topMargin: 5
- top: manualLabel.bottom
- }
- font.pixelSize: Theme.fontSize - 3
- }
- ColumnLayout {
- id: gatewayListColumn
+
+ ScrollView {
+ id: frame
+ clip: true
+ anchors.fill: parent
+ ScrollBar.vertical.policy: ScrollBar.AlwaysOff
+
+ Flickable {
+ id: flickable
+ contentHeight: getManualBoxHeight()
width: parent.width
- spacing: 1
- anchors.top: getManualAnchor()
- Repeater {
- id: gwManualSelectorList
- width: parent.width
- model: root.locationsModel
+ ScrollIndicator.vertical: ScrollIndicator {
+ size: 5
+ contentItem: Rectangle {
+ implicitWidth: 5
+ implicitHeight: 100
+ color: "grey"
+ }
+ }
- RowLayout {
+ Rectangle {
+ anchors {
+ fill: parent
+ margins: 10
+ }
+ Label {
+ id: manualLabel
+ text: manualSelectionLabel
+ font.bold: true
+ }
+ Label {
+ id: bridgeWarning
+ text: onlyBridgesWarning
+ color: "gray"
+ visible: isBridgeSelected()
+ wrapMode: Text.Wrap
+ anchors {
+ topMargin: 5
+ top: manualLabel.bottom
+ }
+ font.pixelSize: Theme.fontSize - 3
+ }
+
+ ColumnLayout {
+ id: gatewayListColumn
width: parent.width
- WrappedRadioButton {
- text: getLocationLabel(modelData)
- location: modelData
- ButtonGroup.group: locsel
- checked: false
- enabled: locationPage.switching ? false : true
- onClicked: {
- if (ctx.status == "on") {
- locationPage.switching = true
+ spacing: 1
+ anchors.top: getManualAnchor()
+
+ Repeater {
+ id: gwManualSelectorList
+ width: parent.width
+ model: root.locationsModel
+
+ RowLayout {
+ width: parent.width
+ WrappedRadioButton {
+ text: getLocationLabel(modelData)
+ location: modelData
+ ButtonGroup.group: locsel
+ checked: false
+ enabled: locationPage.switching ? false : true
+ onClicked: {
+ if (ctx.status == "on") {
+ locationPage.switching = true
+ }
+ root.selectedGateway = location
+ backend.useLocation(location)
+ }
+ }
+ Item {
+ Layout.fillWidth: true
+ }
+ Image {
+ height: 16
+ width: 16
+ visible: isBridgeSelected()
+ source: "../resources/bridge.png"
+ Layout.alignment: Qt.AlignRight
+ Layout.rightMargin: 10
+ }
+ SignalIcon {
+ quality: getSignalFor(modelData)
+ Layout.alignment: Qt.AlignRight
+ Layout.rightMargin: 20
}
- root.selectedGateway = location
- backend.useLocation(location)
}
}
- Item {
- Layout.fillWidth: true
- }
- Image {
- height: 16
- width: 16
- visible: isBridgeSelected()
- source: "../resources/bridge.png"
- Layout.alignment: Qt.AlignRight
- Layout.rightMargin: 10
- }
- SignalIcon {
- // TODO mocked!
- quality: getSignalFor(modelData)
- Layout.alignment: Qt.AlignRight
- Layout.rightMargin: 20
- }
}
}
- }
- }
- }
+ } //flickable
+ } // scrollview
+ } // manualbox
StateGroup {
states: [
@@ -199,7 +220,9 @@ ThemedPage {
}
function getLocationLabel(location) {
- if (!ctx) { return ""}
+ if (!ctx) {
+ return ""
+ }
let l = ctx.locationLabels[location]
return l[0] + ", " + l[1]
}
@@ -213,13 +236,13 @@ ThemedPage {
}
function getSignalFor(location) {
- switch(location) {
- case "amsterdam":
- case "paris":
+ // this is an ad-hoc solution for the no-menshen, riseup case.
+ // when menshen is deployed we'll want to tweak the values for each bucket.
+ let load = ctx.locations[location]
+ switch (true) {
+ case (load > 0.5):
return "good"
- case "newyork":
- return "medium"
- case "montreal":
+ case (load > 0.25):
return "medium"
default:
return "low"
@@ -244,14 +267,14 @@ ThemedPage {
Component.onCompleted: {
if (root.selectedGateway == "auto") {
- autoRadioButton.checked = true;
+ autoRadioButton.checked = true
} else {
let match = false
- for (var i=1; i<locsel.buttons.length; i++) {
+ for (var i = 1; i < locsel.buttons.length; i++) {
let b = locsel.buttons[i]
if (b.location == root.selectedGateway) {
- match = true;
- b.checked = true;
+ match = true
+ b.checked = true
}
}
}
diff --git a/gui/components/Preferences.qml b/gui/components/Preferences.qml
index 2b0aae8..cc25101 100644
--- a/gui/components/Preferences.qml
+++ b/gui/components/Preferences.qml
@@ -34,9 +34,9 @@ ThemedPage {
Rectangle {
id: turnOffWarning
visible: false
- height: 40
+ height: 20
width: parent.width
- color: Theme.bgColor
+ color: "white"
Label {
diff --git a/gui/main.qml b/gui/main.qml
index d8bfb91..0024670 100644
--- a/gui/main.qml
+++ b/gui/main.qml
@@ -2,21 +2,11 @@
/*
TODO (ui rewrite)
- - [x] add systray
- - [x] systray status
- - [x] splash screen
- - [x] splash delay/transitions
- - [x] font: monserrat
- - [x] nested states
- - [x] splash init errors
- - [x] gateway selector
- - [ ] bridges
+ See https://0xacab.org/leap/bitmask-vpn/-/issues/523
+ - [x] udp support
- [ ] minimize/hide from systray
- [ ] control actions from systray
- [ ] add gateway to systray
- - [ ] donation dialog
- - [ ] parse ctx flags (need dialog, etc)
- - [ ] udp support
*/
import QtQuick 2.0
import QtQuick.Controls 2.4
@@ -42,7 +32,6 @@ ApplicationWindow {
minimumHeight: appHeight
maximumHeight: appHeight
-
title: ctx ? ctx.appName : "VPN"
Material.accent: Material.Green
@@ -56,6 +45,7 @@ ApplicationWindow {
// TODO get from persistance
property var selectedGateway: "auto"
+ // FIXME get svg icons
property var icons: {
"off": "qrc:/assets/icon/png/white/vpn_off.png",
"on": "qrc:/assets/icon/png/white/vpn_on.png",
@@ -90,7 +80,6 @@ ApplicationWindow {
id: systray
}
-
Connections {
target: jsonModel
function onDataChanged() {
@@ -100,7 +89,8 @@ ApplicationWindow {
}
ctx = JSON.parse(j)
if (ctx != undefined) {
- locationsModel = Object.keys(ctx.locations)
+ locationsModel = getSortedLocations()
+ console.debug("Got sorted locations:" + locationsModel)
}
if (ctx.errors) {
console.debug("errors, setting root.error")
@@ -109,10 +99,10 @@ ApplicationWindow {
root.error = ""
}
if (ctx.donateURL) {
- isDonationService = true;
+ isDonationService = true
}
if (ctx.donateDialog == 'true') {
- showDonationReminder = true;
+ showDonationReminder = true
}
// TODO check donation
@@ -122,15 +112,9 @@ ApplicationWindow {
// // move this to onClick of "close" for widget
// backend.donateSeen();
//}
- // TODO refactor donate widget into main view (with close window!)
- //if (ctx.status == "on") {
- // gwNextConnectionText.visible = false
- // gwReconnectText.visible = false
- // when: vpn.status == "on"
- //}
/*
- TODO libraries need login
+ TODO libraries need login
if (ctx.loginDialog == 'true') {
login.visible = true
}
@@ -141,6 +125,23 @@ ApplicationWindow {
}
}
+ function getSortedLocations() {
+ let obj = ctx.locations
+ var arr = []
+ for (var prop in obj) {
+ if (obj.hasOwnProperty(prop)) {
+ arr.push({
+ "key": prop,
+ "value": obj[prop]
+ })
+ }
+ }
+ arr.sort(function (a, b) {
+ return a.value - b.value
+ }).reverse()
+ return Array.from(arr, (k,_) => k.key);
+ }
+
onSceneGraphError: function (error, msg) {
console.debug("ERROR while initializing scene")
console.debug(msg)
diff --git a/pkg/vpn/bonafide/gateways.go b/pkg/vpn/bonafide/gateways.go
index 4299bb2..633493e 100644
--- a/pkg/vpn/bonafide/gateways.go
+++ b/pkg/vpn/bonafide/gateways.go
@@ -96,14 +96,18 @@ func (p *gatewayPool) listLocationFullness(transport string) map[string]float64
return cm
}
if len(p.recommended) != 0 {
- for _, gw := range p.recommended {
+ for idx, gw := range p.recommended {
if gw.gateway.Transport != transport {
continue
}
if _, ok := cm[gw.gateway.Location]; ok {
continue
}
- cm[gw.gateway.Location] = gw.Fullness
+ if gw.Fullness != -1 {
+ cm[gw.gateway.Location] = gw.Fullness
+ } else {
+ cm[gw.gateway.Location] = 1 - float64(idx)/float64(len(p.recommended))
+ }
}
} else {
for _, location := range locations {