Skip to content
Snippets Groups Projects
main.qml 16.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • Kali Kaneko's avatar
    Kali Kaneko committed
    import QtQuick 2.9
    
    import QtQuick.Dialogs 1.2 // TODO use native dialogs in labs.platform
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    import QtQuick.Layouts 1.12
    
    import QtQuick.Controls 2.4
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    import Qt.labs.platform 1.0
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
    
    import "logic.js" as Logic
    
    
    ApplicationWindow {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        id: app
    
        width: 300
    
        height: 600
    
        maximumWidth: 300
    
        minimumWidth: 300
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        maximumHeight: 500
        minimumHeight: 300
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        property var ctx
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        property var loginDone
    
        property var allowEmptyPass
    
        property var needsRestart
    
        property var needsDonate
        property var shownDonate
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
    
        onSceneGraphError: function(error, msg) {
            console.debug("ERROR while initializing scene")
            console.debug(msg)
        }
    
    
        MainBar {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            id: bar
        }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        StackLayout {
    
            anchors.fill: parent
            currentIndex: bar.currentIndex
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                id: infoTab
                anchors.centerIn: parent
    
    
                BackgroundImage {
    
                    id: background
                }
    
                Item {
                    id: connBox
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    anchors.centerIn: parent
    
                    width: 300
                    height: 300
    
                    Rectangle {
                        anchors.fill: parent
                        color: "white"
                        opacity: 0.3
                        layer.enabled: true
    
                    Column {
    
                        anchors.centerIn: parent
                        spacing: 5
    
                        Text {
                            id: mainStatus
                            text: "off"
                            font.pixelSize: 26
                            anchors.horizontalCenter: parent.horizontalCenter
                        }
    
                        Text {
                            id: mainCurrentGateway
                            text: ""
                            font.pixelSize: 20
                            anchors.horizontalCenter: parent.horizontalCenter
                        }
    
    
                        VPNSwitch {
    
                            id: vpntoggle
    
                            Connections {
                                function onCheckedChanged() {
                                    if (vpntoggle.checked == true
                                            && ctx.status == "off") {
                                        backend.switchOn()
    
                                        vpntoggle.checkable = false
    
                                    }
                                    if (vpntoggle.checked === false
                                            && ctx.status == "on") {
                                        backend.switchOff()
    
                                        vpntoggle.checkable = false
    
                        LocationText {
    
                            id: manualOverrideWarning
                            visible: isManualLocation()
    
                    } // end column
                } // end inner item
            } // end outer item
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
            Item {
    
                id: gatewayTab
                anchors.centerIn: parent
    
                Column {
    
                    anchors.centerIn: parent
                    spacing: 10
    
                    //width: parent.width
    
                    RadioButton {
                        id: autoSelectionButton
    
                        checked: !isManualLocation()
    
                        text: qsTr("Recommended")
    
                        onClicked: {
                            backend.useAutomaticGateway()
                            manualSelectionItem.checked = false
                        }
    
                    RadioButton {
                        id: manualSelectionButton
    
                        checked: isManualLocation()
    
                        text: qsTr("Manual")
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                        onClicked: setGwSelection()
    
                    ComboBox {
                        id: gwSelector
                        editable: false
    
                        visible: manualSelectionButton.checked
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                        anchors.horizontalCenter: parent.horizontalCenter
    
    
                        model: [qsTr("Recommended")]
    
                        onActivated: {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                            console.debug("Selected gateway:", currentText)
    
                            if (ctx.status == "off") {
                                gwNextConnectionText.visible = true
                            }
                            if (ctx.status == "on") {
                                gwReconnectText.visible = true
                            }
    
                            backend.useLocation(currentText.toString())
    
                            manualSelectionItem.checked = true
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                        delegate: ItemDelegate {
                            // TODO: we could use icons
                            // https://doc.qt.io/qt-5/qml-qtquick-controls2-abstractbutton.html#icon-prop
                            background: Rectangle {
                                color: {
                                    "#ffffff"
                                    // FIXME locations is not defined when we launch
                                    /*
                                    const fullness = ctx.locations[modelData]
                                    if (fullness >= 0 && fullness < 0.4) {
                                        "#83fc5a"
                                    } else if (fullness >= 0.4 && fullness < 0.75) {
                                        "#fcb149"
                                    } else if (fullness >= 0.75) {
                                        "#fc5a5d"
                                    } else {
                                        "#ffffff"
                                    }
                                    */
                                }
                            }
                            contentItem: Text {
                                text: modelData
                                font: gwSelector.font
                                color: "#000000"
                            }
                        }
    
    
                    }
    
                    Text {
                        id: gwReconnectText
                        anchors.horizontalCenter: parent.horizontalCenter
                        width: 180
                        font.pixelSize: 12
                        color: "green"
                        wrapMode: Text.WordWrap 
                        text: qsTr("Reconnecting to the selected gateway…")
                        visible: false
                    }
    
                    Text {
                        id: gwNextConnectionText
                        anchors.horizontalCenter: parent.horizontalCenter
                        width: 180
                        font.pixelSize: 12
                        color: "green"
                        wrapMode: Text.WordWrap 
                        text: qsTr("This gateway will be used for next connection.")
                        visible: false
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                } // end column
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            } // end item
    
            BridgesItem {
    
                id: bridgesTab
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        } // end stacklayout
    
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        Connections {
            target: jsonModel
    
    meskio's avatar
    meskio committed
            function onDataChanged() {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                ctx = JSON.parse(jsonModel.getJson())
    
                // TODO pass QML_DEBUG variable to be hyper-verbose
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                //console.debug(jsonModel.getJson())
    
                gwSelector.model = Object.keys(ctx.locations)
    
                if (ctx.donateDialog == 'true') {
    
                    Logic.setNeedsDonate(true);
    
                if (ctx.loginDialog == 'true') {
    
                    console.debug(jsonModel.getJson())
                    console.debug("DEBUG: should display login")
    
                    login.visible = true
                }
    
                if (ctx.loginOk == 'true') {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    loginOk.visible = true
                }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                if (ctx.errors) {
                    login.visible = false
                    if (ctx.errors == "nohelpers") {
                        showInitFailure(
                                    qsTr("Could not find helpers. Please check your installation"))
                    } else if (ctx.errors == "nopolkit") {
                        showInitFailure(qsTr("Could not find polkit agent."))
                    } else {
                        showInitFailure()
                    }
    
                if (ctx.donateURL) {
                    donateItem.visible = true
                }
    
    
                if (ctx.status == "on") {
                    gwNextConnectionText.visible = false
                    gwReconnectText.visible = false
                }
    
        function showInitFailure(msg) {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            console.debug("ERRORS:", ctx.errors)
            if (msg == undefined) {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                if (ctx.errors == 'bad_auth_502'
                        || ctx.errors == 'bad_auth_timeout') {
                    msg = qsTr("Oops! The authentication service seems down. Please try again later")
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    initFailure.title = qsTr("Service Error")
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                } else if (ctx.errors == 'bad_auth') {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    if (allowEmptyPass) {
                        // For now, this is a libraryVPN, so we can be explicit about what credentials are here.
                        // Another option to consider is to customize the error strings while vendoring.
                        //: Incorrect library card number
                        msg = qsTr("Please check your Patron ID")
                    } else {
                        msg = qsTr("Could not log in with those credentials, please retry")
                    }
                    initFailure.title = qsTr("Login Error")
                } else {
                    //: %1 -> application name
                    //: %2 -> error string
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    msg = qsTr("Got an error starting %1: %2").arg(ctx.appName).arg(
                                ctx.errors)
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                }
            }
            initFailure.text = msg
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            initFailure.visible = true
    
        function isManualLocation() {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            if (!ctx) {
    
                return false
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            }
            return ctx.manualLocation == "true"
    
        function hasMultipleGateways() {
            // could also count the gateways
            let provider = Logic.getSelectedProvider(providers);
            if (provider == "riseup") {
                return true;
            } else {
                return false;
            }
        }
    
    
        function setGwSelection() {
    
            if (!isManualLocation()) {
                manualSelectionItem.checked = false
                bar.currentIndex = 1
                app.visible = true
                app.show()
                app.raise()
                return
            }
            // last used manual selection
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            const location = ctx.currentLocation.toLowerCase()
            const idx = gwSelector.model.indexOf(location)
            gwSelector.currentIndex = idx
            backend.useLocation(location)
    
        function showMainWindow() {
                bar.currentIndex = 0
                app.visible = true
                app.show()
                app.raise()
        }
    
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        Component.onCompleted: {
    
            Logic.debugInit()
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            loginDone = false
    
            allowEmptyPass = Logic.shouldAllowEmptyPass(providers)
    
            needsRestart = false;
    
    
            /* this is a temporary workaround until general GUI revamp for 0.21.8 */
            let provider = Logic.getSelectedProvider(providers);
            if (provider == "calyx") {
                background.color = "#8EA844";
    
                background.backgroundVisible = false;
                gwSelector.visible = false;
                manualSelectionButton.visible = false;
    
            if (!systrayAvailable) {
              app.visible = true
              app.raise()
            }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        property var icons: {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            "off": "qrc:/assets/icon/png/white/vpn_off.png",
            "on": "qrc:/assets/icon/png/white/vpn_on.png",
            "wait": "qrc:/assets/icon/png/white/vpn_wait_0.png",
            "blocked": "qrc:/assets/icon/png/white/vpn_blocked.png"
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
    
        VpnState {
            id: vpn
        }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        SystemTrayIcon {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
            id: systray
    
            visible: systrayVisible
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            /* the systray menu cannot be buried in a child qml file because
             * otherwise the ids are not available
             * from other components
             */
            menu: Menu {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuItem {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    id: statusItem
    
    kwadronaut's avatar
    kwadronaut committed
                    text: qsTr("Checking status…")
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    enabled: false
                }
    
    
                MenuSeparator {}
    
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuItem {
                    id: autoSelectionItem
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    text: qsTr("Recommended")
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    checkable: true
    
                    checked: !isManualLocation()
                    onTriggered: {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                        backend.useAutomaticGateway()
    
                        manualSelectionItem.checked = false
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                }
    
                /* a minimal segfault for submenu */
                // Menu {}
                MenuItem {
                    id: manualSelectionItem
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    text: {
    
                        if (isManualLocation() != "") {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                            locationStr()
                        } else {
                            qsTr("Pick location…")
                        }
                    }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    checkable: true
    
                    checked: isManualLocation()
                    onTriggered: setGwSelection()
    
                    visible: hasMultipleGateways()
                }
    
                MenuItem {
                    text: qsTr("Preferences…")
                    visible: !hasMultipleGateways()
    
                    onTriggered: showMainWindow()
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                }
    
                MenuSeparator {}
    
                MenuItem {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    text: {
                        if (vpn.state == "failed")
                            qsTr("Reconnect")
                        else
                            qsTr("Turn on")
                    }
                    onTriggered: {
                        backend.switchOn()
                    }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    visible: ctx ? (ctx.status == "off"
                                    || ctx.status == "failed") : false
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuItem {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    text: {
                        if (ctx && ctx.status == "starting")
                            qsTr("Cancel")
                        else
                            qsTr("Turn off")
                    }
                    onTriggered: {
                        backend.switchOff()
                    }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    visible: ctx ? (ctx.status == "on" || ctx.status == "starting"
                                    || ctx.status == "failed") : false
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuSeparator {}
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuItem {
    
    kwadronaut's avatar
    kwadronaut committed
                    text: qsTr("About…")
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    onTriggered: {
                        about.visible = true
    
                        requestActivate()
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuItem {
    
                    id: donateItem
    
    kwadronaut's avatar
    kwadronaut committed
                    text: qsTr("Donate…")
    
                    visible: ctx ? ctx.donateURL : false
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    onTriggered: {
                        donate.visible = true
                    }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuSeparator {}
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuItem {
    
    kwadronaut's avatar
    kwadronaut committed
                    text: qsTr("Help…")
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                        console.debug(Qt.resolvedUrl(ctx.helpURL))
    
                        Qt.openUrlExternally(Qt.resolvedUrl(ctx.helpURL))
                    }
                }
    
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuItem {
    
    kwadronaut's avatar
    kwadronaut committed
                    text: qsTr("Report a bug…")
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                        Qt.openUrlExternally(
                                    Qt.resolvedUrl(
                                        "https://0xacab.org/leap/bitmask-vpn/issues"))
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuSeparator {}
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuItem {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    text: qsTr("Quit")
                    onTriggered: backend.quit()
                }
            }
    
    
            Component.onCompleted: {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                systray.icon.source = icons["off"]
    
                tooltip = qsTr("Checking status…")
                console.debug("systray init completed")
                if (systrayVisible) {
                    console.log("show systray")
                    if (Qt.platform.os === "windows") {
                        let appname = ctx ? ctx.appName : "VPN"
    
                        Logic.showNotification(
                            ctx,
                            appname
                            + " is up and running. Please use system tray icon to control it.")
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        }
    
        DonateDialog {
            id: donate
    
            visible: false
    
        AboutDialog {
            id: about
            visible: false
        }
    
        LoginDialog {
            id: login
    
            visible: false
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        LoginOKDialog {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            id: loginOk
            visible: false
        }
    
    
        MessageDialog {
            id: errorStartingVPN
            modality: Qt.NonModal
    
            title: qsTr("Error starting VPN")
            text: ""
            detailedText: ""
            visible: false
    
        MessageDialog {
            id: authAgent
            modality: Qt.NonModal
    
            title: qsTr("Missing authentication agent")
            text: qsTr("Could not find a polkit authentication agent. Please run one and try again.")
            visible: false
    
            visible: false
    
    
        function locationStr() {
    
            if (ctx.currentLocation && ctx.currentCountry) {
                return ctx.currentLocation + ", " + ctx.currentCountry
            } else {
                return ""
            }
    
        function useBridges(value) {
            if (value==true) {
                backend.setTransport("obfs4")
            } else {
                backend.setTransport("openvpn")
            }
        }
    
    
        property alias brReconnect:bridgesTab.displayReconnect