diff --git a/libnymea-app-core/connection/awsclient.cpp b/libnymea-app-core/connection/awsclient.cpp index fd74326f..ef752798 100644 --- a/libnymea-app-core/connection/awsclient.cpp +++ b/libnymea-app-core/connection/awsclient.cpp @@ -660,6 +660,7 @@ void AWSClient::fetchCertificate(const QString &uuid, std::functionget(request); + qDebug() << "Fetching certificate for vendor:" << m_configs.at(m_usedConfigIndex).certificateVendorId << "device id:" << fixedUuid; connect(reply, &QNetworkReply::finished, this, [this, reply, callback]() { reply->deleteLater(); QByteArray data = reply->readAll(); diff --git a/nymea-app/styles/dark/ApplicationWindow.qml b/nymea-app/styles/dark/ApplicationWindow.qml index 6ace480d..17031ca4 100644 --- a/nymea-app/styles/dark/ApplicationWindow.qml +++ b/nymea-app/styles/dark/ApplicationWindow.qml @@ -32,4 +32,6 @@ ApplicationWindow { "conductivitysensor": "green", "pressuresensor": "grey" } + + property bool showConnectionTabs: false } diff --git a/nymea-app/styles/light/ApplicationWindow.qml b/nymea-app/styles/light/ApplicationWindow.qml index 3e1dd14e..c4fa8a8c 100644 --- a/nymea-app/styles/light/ApplicationWindow.qml +++ b/nymea-app/styles/light/ApplicationWindow.qml @@ -30,4 +30,7 @@ ApplicationWindow { "conductivitysensor": "green", "pressuresensor": "grey" } + + property bool showConnectionTabs: false + } diff --git a/nymea-app/ui/RootItem.qml b/nymea-app/ui/RootItem.qml index bd66525b..b43b268a 100644 --- a/nymea-app/ui/RootItem.qml +++ b/nymea-app/ui/RootItem.qml @@ -3,208 +3,279 @@ import QtQuick.Controls 2.2 import QtQuick.Controls.Material 2.2 import QtQuick.Layouts 1.3 import Nymea 1.0 +import "components" Item { id: root - Engine { - id: engine - } - // If you need to assign to a property "engine", `engine: engine` won't work, as a workaround, use `engine: _engine` - // TODO: Improve this situation - property alias _engine: engine - - Binding { - target: engine.awsClient - property: "config" - value: settings.cloudEnvironment - } - // Workaround flickering on pageStack animations when the white background shines through Rectangle { anchors.fill: parent color: Material.background } - StackView { - id: pageStack - objectName: "pageStack" - anchors.fill: parent - initialItem: Page {} -// onDepthChanged: { -// print("stackview depth changed", pageStack.depth) -// } - } - - Component.onCompleted: { - pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml")) - setupPushNotifications(); - } - - function init() { - print("calling init. Auth required:", engine.jsonRpcClient.authenticationRequired, "initial setup required:", engine.jsonRpcClient.initialSetupRequired, "jsonrpc connected:", engine.jsonRpcClient.connected) - pageStack.clear() - if (!engine.connection.connected) { - pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml")) - return; + ListModel { + id: tabModel + Component.onCompleted: { + append({}) } - - if (engine.jsonRpcClient.authenticationRequired || engine.jsonRpcClient.initialSetupRequired) { - if (engine.jsonRpcClient.pushButtonAuthAvailable) { - print("opening push button auth") - var page = pageStack.push(Qt.resolvedUrl("PushButtonAuthPage.qml")) - page.backPressed.connect(function() { - settings.lastConnectedHost = ""; - engine.connection.disconnect(); - init(); - }) - } else { - var page = pageStack.push(Qt.resolvedUrl("LoginPage.qml")); - page.backPressed.connect(function() { - settings.lastConnectedHost = ""; - engine.connection.disconnect() - init(); - }) - } - } else if (engine.jsonRpcClient.connected) { - pageStack.push(Qt.resolvedUrl("MainPage.qml")) - } else { - pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml")) - } - } - - function handleCloseEvent(close) { - if (Qt.platform.os == "android") { - // If we're connected, allow going back up to MainPage - if ((engine.jsonRpcClient.connected && pageStack.depth > 1) - // if we're not connected, only allow using the back button in wizards - || (!engine.jsonRpcClient.connected && pageStack.depth > 3)) { - close.accepted = false; - pageStack.pop(); - } - } - } - - function setupPushNotifications(askForPermissions) { - if (askForPermissions === undefined) { - askForPermissions = true; - } - - if (!engine.awsClient.isLoggedIn) { - print("AWS not logged in. Cannot register for push"); - return; - } - - if (PushNotifications.token.length === 0) { - print("Don't have a token yet. Cannot register for push"); - return; - } - - if (!PlatformHelper.hasPermissions) { - if (askForPermissions) { - PlatformHelper.requestPermissions(); - } - } else { - engine.awsClient.registerPushNotificationEndpoint(PushNotifications.token, PlatformHelper.deviceManufacturer + " " + PlatformHelper.deviceModel, PlatformHelper.deviceSerial + "+io.guh.nymeaapp"); - } - } - - Connections { - target: engine.jsonRpcClient - onConnectedChanged: { - print("json client connected changed", engine.jsonRpcClient.connected) - if (engine.jsonRpcClient.connected) { - settings.lastConnectedHost = engine.connection.url - } - init(); - } - - onAuthenticationRequiredChanged: { - print("auth required changed") - init(); - } - onInitialSetupRequiredChanged: { - print("setup required changed") - init(); - } - - onInvalidProtocolVersion: { - var popup = invalidVersionComponent.createObject(app.contentItem); - popup.actualVersion = actualVersion; - popup.minimumVersion = minimumVersion - popup.open() + function addTab() { settings.lastConnectedHost = "" + tabModel.append({}) } } - Connections { - target: Qt.application - enabled: engine.jsonRpcClient.connected && settings.returnToHome - onStateChanged: { - print("App active state changed:", state) - if (state !== Qt.ApplicationActive) { - init(); + ColumnLayout { + anchors.fill: parent + + SwipeView { + id: swipeView + Layout.fillHeight: true + Layout.fillWidth: true + interactive: false + + Repeater { + id: mainRepeater + model: tabModel + + delegate: StackView { + id: pageStack + height: swipeView.height + width: swipeView.width + objectName: "pageStack" + initialItem: Page {} + + Engine { + id: engine + } + property alias _engine: engine + + Component.onCompleted: { + pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml")) + setupPushNotifications(); + } + + function init() { + print("calling init. Auth required:", engine.jsonRpcClient.authenticationRequired, "initial setup required:", engine.jsonRpcClient.initialSetupRequired, "jsonrpc connected:", engine.jsonRpcClient.connected) + pageStack.clear() + if (!engine.connection.connected) { + pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml")) + return; + } + + if (engine.jsonRpcClient.authenticationRequired || engine.jsonRpcClient.initialSetupRequired) { + if (engine.jsonRpcClient.pushButtonAuthAvailable) { + print("opening push button auth") + var page = pageStack.push(Qt.resolvedUrl("PushButtonAuthPage.qml")) + page.backPressed.connect(function() { + settings.lastConnectedHost = ""; + engine.connection.disconnect(); + init(); + }) + } else { + var page = pageStack.push(Qt.resolvedUrl("LoginPage.qml")); + page.backPressed.connect(function() { + settings.lastConnectedHost = ""; + engine.connection.disconnect() + init(); + }) + } + } else if (engine.jsonRpcClient.connected) { + pageStack.push(Qt.resolvedUrl("MainPage.qml")) + } else { + pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml")) + } + } + + function handleCloseEvent(close) { + if (Qt.platform.os == "android") { + // If we're connected, allow going back up to MainPage + if ((engine.jsonRpcClient.connected && pageStack.depth > 1) + // if we're not connected, only allow using the back button in wizards + || (!engine.jsonRpcClient.connected && pageStack.depth > 3)) { + close.accepted = false; + pageStack.pop(); + } + } + } + + function setupPushNotifications(askForPermissions) { + if (askForPermissions === undefined) { + askForPermissions = true; + } + + if (!engine.awsClient.isLoggedIn) { + print("AWS not logged in. Cannot register for push"); + return; + } + + if (PushNotifications.token.length === 0) { + print("Don't have a token yet. Cannot register for push"); + return; + } + + if (!PlatformHelper.hasPermissions) { + if (askForPermissions) { + PlatformHelper.requestPermissions(); + } + } else { + engine.awsClient.registerPushNotificationEndpoint(PushNotifications.token, PlatformHelper.deviceManufacturer + " " + PlatformHelper.deviceModel, PlatformHelper.deviceSerial + "+io.guh.nymeaapp"); + } + } + + Connections { + target: engine.jsonRpcClient + onConnectedChanged: { + print("json client connected changed", engine.jsonRpcClient.connected) + if (engine.jsonRpcClient.connected) { + settings.lastConnectedHost = engine.connection.url + } + init(); + } + + onAuthenticationRequiredChanged: { + print("auth required changed") + init(); + } + onInitialSetupRequiredChanged: { + print("setup required changed") + init(); + } + + onInvalidProtocolVersion: { + var popup = invalidVersionComponent.createObject(app.contentItem); + popup.actualVersion = actualVersion; + popup.minimumVersion = minimumVersion + popup.open() + settings.lastConnectedHost = "" + } + } + + Connections { + target: Qt.application + enabled: engine.jsonRpcClient.connected && settings.returnToHome + onStateChanged: { + print("App active state changed:", state) + if (state !== Qt.ApplicationActive) { + init(); + } + } + } + + Connections { + target: PlatformHelper + onHasPermissionsChanged: { + setupPushNotifications(false) + } + } + + Connections { + target: PushNotifications + onTokenChanged: { + setupPushNotifications(); + } + } + + Connections { + target: engine.awsClient + onIsLoggedInChanged: { + setupPushNotifications() + } + } + + Component { + id: invalidVersionComponent + Popup { + id: popup + + property string actualVersion: "0.0" + property string minimumVersion: "1.0" + + width: app.width * .8 + height: col.childrenRect.height + app.margins * 2 + x: (app.width - width) / 2 + y: (app.height - height) / 2 + visible: false + ColumnLayout { + id: col + anchors { left: parent.left; right: parent.right } + spacing: app.margins + Label { + text: qsTr("Connection error") + Layout.fillWidth: true + font.pixelSize: app.largeFont + } + Label { + text: qsTr("Sorry, the version of the %1 box you are trying to connect to is too old. This app requires at least version %2 but the %1 box only supports %3").arg(app.systemName).arg(popup.minimumVersion).arg(popup.actualVersion) + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + Button { + Layout.fillWidth: true + text: qsTr("OK") + onClicked: { + engine.connection.disconnect(); + popup.close() + } + } + } + } + } + } } } - } - Connections { - target: PlatformHelper - onHasPermissionsChanged: { - setupPushNotifications(false) - } - } + RowLayout { + visible: app.showConnectionTabs - Connections { - target: PushNotifications - onTokenChanged: { - setupPushNotifications(); - } - } + TabBar { + id: tabbar + Layout.fillWidth: true - Connections { - target: engine.awsClient - onIsLoggedInChanged: { - setupPushNotifications() - } - } + Repeater { + model: mainRepeater.count - Component { - id: invalidVersionComponent - Popup { - id: popup + delegate: TabButton { + id: hostTabButton + property var engine: mainRepeater.itemAt(index)._engine + property string serverName: engine.basicConfiguration.serverName - property string actualVersion: "0.0" - property string minimumVersion: "1.0" + contentItem: RowLayout { + Label { + Layout.fillWidth: true + text: hostTabButton.serverName !== "" ? hostTabButton.serverName : qsTr("New connection") + elide: Text.ElideRight + } + ColorIcon { + Layout.fillHeight: true + Layout.preferredWidth: height + visible: tabModel.count > 1 + name: "../images/close.svg" + MouseArea { + anchors.fill: parent + onClicked: tabModel.remove(index) + } + } + } - width: app.width * .8 - height: col.childrenRect.height + app.margins * 2 - x: (app.width - width) / 2 - y: (app.height - height) / 2 - visible: false - ColumnLayout { - id: col - anchors { left: parent.left; right: parent.right } - spacing: app.margins - Label { - text: qsTr("Connection error") - Layout.fillWidth: true - font.pixelSize: app.largeFont - } - Label { - text: qsTr("Sorry, the version of the %1 box you are trying to connect to is too old. This app requires at least version %2 but the %1 box only supports %3").arg(app.systemName).arg(popup.minimumVersion).arg(popup.actualVersion) - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - Button { - Layout.fillWidth: true - text: qsTr("OK") - onClicked: { - engine.connection.disconnect(); - popup.close() + onClicked: { + swipeView.currentIndex = index + } } } } + + TabButton { + Layout.preferredWidth: height + contentItem: ColorIcon { + height: parent.height + width: parent.width + name: "../images/add.svg" + } + onClicked: { + tabModel.addTab() + } + } } } } diff --git a/nymea-app/ui/components/FancyHeader.qml b/nymea-app/ui/components/FancyHeader.qml index dceb7cf6..33de4258 100644 --- a/nymea-app/ui/components/FancyHeader.qml +++ b/nymea-app/ui/components/FancyHeader.qml @@ -11,6 +11,8 @@ ToolBar { property string title property alias model: menuRepeater.model + property bool showNewTabButton: false + signal clicked(int index); QtObject {