diff --git a/storybook/pages/OnboardingLayoutPage.qml b/storybook/pages/OnboardingLayoutPage.qml index 3ba126e39ab..47c97badda3 100644 --- a/storybook/pages/OnboardingLayoutPage.qml +++ b/storybook/pages/OnboardingLayoutPage.qml @@ -44,6 +44,7 @@ SplitView { id: onboarding SplitView.fillWidth: true SplitView.fillHeight: true + networkChecksEnabled: true onboardingStore: OnboardingStore { readonly property int keycardState: ctrlKeycardState.currentValue // enum Onboarding.KeycardState property int keycardRemainingPinAttempts: 5 diff --git a/storybook/qmlTests/tests/tst_OnboardingLayout.qml b/storybook/qmlTests/tests/tst_OnboardingLayout.qml index df3776cfcf6..0a65c829355 100644 --- a/storybook/qmlTests/tests/tst_OnboardingLayout.qml +++ b/storybook/qmlTests/tests/tst_OnboardingLayout.qml @@ -34,6 +34,7 @@ Item { OnboardingLayout { anchors.fill: parent + networkChecksEnabled: false onboardingStore: OnboardingStore { readonly property int keycardState: mockDriver.keycardState // enum Onboarding.KeycardState property int keycardRemainingPinAttempts: 5 diff --git a/ui/StatusQ/include/StatusQ/networkchecker.h b/ui/StatusQ/include/StatusQ/networkchecker.h index 39e4f91c3ff..fb8bc24b0f0 100644 --- a/ui/StatusQ/include/StatusQ/networkchecker.h +++ b/ui/StatusQ/include/StatusQ/networkchecker.h @@ -1,41 +1,51 @@ #pragma once -#include #include #include +#include #include -#include - -using namespace std::chrono_literals; +class QNetworkAccessManager; /// Checks if the internet connection is available, when active. -/// It checks the connection every 30 seconds as long as the \c active property is \c true. -class NetworkChecker : public QObject +/// It checks the connection every 30 seconds as long as the \c active property is \c true (by default it is) +class NetworkChecker : public QObject, public QQmlParserStatus { Q_OBJECT - Q_PROPERTY(bool isOnline READ isOnline NOTIFY isOnlineChanged) - Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged) + Q_INTERFACES(QQmlParserStatus) + + Q_PROPERTY(bool isOnline READ isOnline NOTIFY isOnlineChanged FINAL) + Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged FINAL) + Q_PROPERTY(bool checking READ checking NOTIFY checkingChanged FINAL) public: - explicit NetworkChecker(QObject* parent = nullptr); + explicit NetworkChecker(QObject *parent = nullptr); bool isOnline() const; bool isActive() const; void setActive(bool active); + Q_INVOKABLE void checkNetwork(); + +protected: + void classBegin() override; + void componentComplete() override; + signals: void isOnlineChanged(bool online); void activeChanged(bool active); + void checkingChanged(); private: QNetworkAccessManager manager; QTimer timer; bool online = false; bool active = true; - constexpr static std::chrono::milliseconds checkInterval = 30s; - void checkNetwork(); - void onFinished(QNetworkReply* reply); + void onFinished(QNetworkReply *reply); void updateRegularCheck(bool active); -}; \ No newline at end of file + + bool m_checking{false}; + bool checking() const; + void setChecking(bool checking); +}; diff --git a/ui/StatusQ/src/networkchecker.cpp b/ui/StatusQ/src/networkchecker.cpp index 204a4b1f46f..0922d805ae1 100644 --- a/ui/StatusQ/src/networkchecker.cpp +++ b/ui/StatusQ/src/networkchecker.cpp @@ -1,12 +1,17 @@ #include "StatusQ/networkchecker.h" -NetworkChecker::NetworkChecker(QObject* parent) +namespace { +using namespace std::chrono_literals; + +constexpr static auto checkInterval = 30s; +} + +NetworkChecker::NetworkChecker(QObject *parent) : QObject(parent) { + manager.setTransferTimeout(); connect(&manager, &QNetworkAccessManager::finished, this, &NetworkChecker::onFinished); connect(&timer, &QTimer::timeout, this, &NetworkChecker::checkNetwork); - - updateRegularCheck(active); } bool NetworkChecker::isOnline() const @@ -18,16 +23,26 @@ void NetworkChecker::checkNetwork() { QNetworkRequest request(QUrl(QStringLiteral("http://fedoraproject.org/static/hotspot.txt"))); manager.get(request); + setChecking(true); +} + +void NetworkChecker::classBegin() +{ + // empty on purpose +} + +void NetworkChecker::componentComplete() { + updateRegularCheck(active); } -void NetworkChecker::onFinished(QNetworkReply* reply) +void NetworkChecker::onFinished(QNetworkReply *reply) { - bool wasOnline = online; + setChecking(false); + const auto wasOnline = online; online = (reply->error() == QNetworkReply::NoError); reply->deleteLater(); - if(wasOnline != online) - { + if (wasOnline != online) { emit isOnlineChanged(online); } } @@ -39,7 +54,8 @@ bool NetworkChecker::isActive() const void NetworkChecker::setActive(bool active) { - if(active == this->active) return; + if (active == this->active) + return; this->active = active; emit activeChanged(active); @@ -49,13 +65,24 @@ void NetworkChecker::setActive(bool active) void NetworkChecker::updateRegularCheck(bool active) { - if(active) - { + if (active) { checkNetwork(); timer.start(checkInterval); - } - else - { + } else { timer.stop(); } -} \ No newline at end of file +} + +bool NetworkChecker::checking() const +{ + return m_checking; +} + +void NetworkChecker::setChecking(bool checking) +{ + if (m_checking == checking) + return; + + m_checking = checking; + emit checkingChanged(); +} diff --git a/ui/app/AppLayouts/Onboarding2/OnboardingLayout.qml b/ui/app/AppLayouts/Onboarding2/OnboardingLayout.qml index c43511c9460..fe9aa0f25ff 100644 --- a/ui/app/AppLayouts/Onboarding2/OnboardingLayout.qml +++ b/ui/app/AppLayouts/Onboarding2/OnboardingLayout.qml @@ -26,6 +26,7 @@ Page { property int splashScreenDurationMs: 30000 property bool biometricsAvailable: Qt.platform.os === Constants.mac + required property bool networkChecksEnabled readonly property alias stack: stack readonly property alias primaryFlow: d.primaryFlow // Onboarding.PrimaryFlow enum diff --git a/ui/app/AppLayouts/Onboarding2/pages/LoginPage.qml b/ui/app/AppLayouts/Onboarding2/pages/LoginPage.qml index 57708f8cc43..145c69f10a7 100644 --- a/ui/app/AppLayouts/Onboarding2/pages/LoginPage.qml +++ b/ui/app/AppLayouts/Onboarding2/pages/LoginPage.qml @@ -3,6 +3,7 @@ import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import QtQml.Models 2.15 +import StatusQ 0.1 import StatusQ.Core 0.1 import StatusQ.Components 0.1 import StatusQ.Controls 0.1 @@ -17,6 +18,8 @@ import utils 1.0 OnboardingPage { id: root + required property bool networkChecksEnabled + title: qsTr("Log in") signal loginWithSeedphraseRequested() @@ -119,6 +122,11 @@ OnboardingPage { } } + NetworkChecker { + id: netChecker + active: root.networkChecksEnabled + } + Component { id: loginWithSyncAck StatusDialog { @@ -127,6 +135,7 @@ OnboardingPage { width: 480 padding: 20 destroyOnClose: true + onOpened: if (root.networkChecksEnabled) netChecker.checkNetwork() contentItem: ColumnLayout { spacing: 20 StatusBaseText { @@ -169,7 +178,11 @@ OnboardingPage { text: qsTr("Continue") enabled: ack1.checked && ack2.checked && ack3.checked onClicked: { - root.loginWithSyncingRequested() + if (root.networkChecksEnabled && !netChecker.isOnline) { + networkCheckPopup.createObject(root, {netChecker}).open() + } else { + root.loginWithSyncingRequested() + } close() } } @@ -177,4 +190,101 @@ OnboardingPage { } } } + + + + Component { + id: networkCheckPopup + StatusDialog { + objectName: "networkCheckPopup" + title: qsTr("Status does not have access to local network") + width: 480 + padding: 20 + destroyOnClose: true + + required property var netChecker + + contentItem: ColumnLayout { + spacing: 20 + StatusBaseText { + Layout.fillWidth: true + wrapMode: Text.Wrap + text: qsTr("Status must be connected to the local network on this device for you to be able to log in via syncing. To rectify this...") + } + OnboardingFrame { + Layout.fillWidth: true + dropShadow: false + cornerRadius: Theme.radius + horizontalPadding: 20 + verticalPadding: 12 + contentItem: ColumnLayout { + spacing: 12 + StatusBaseText { + Layout.fillWidth: true + wrapMode: Text.Wrap + color: Theme.palette.baseColor1 + text: qsTr("1. Open System Settings") + } + StatusBaseText { + Layout.fillWidth: true + wrapMode: Text.Wrap + color: Theme.palette.baseColor1 + text: qsTr("2. Click Privacy & Security") + } + StatusBaseText { + Layout.fillWidth: true + wrapMode: Text.Wrap + color: Theme.palette.baseColor1 + text: qsTr("3. Click Local Network") + } + StatusBaseText { + Layout.fillWidth: true + wrapMode: Text.Wrap + color: Theme.palette.baseColor1 + text: qsTr("4. Find Status") + } + StatusBaseText { + Layout.fillWidth: true + wrapMode: Text.Wrap + color: Theme.palette.baseColor1 + text: qsTr("5. Toggle the switch to grant access") + } + StatusBaseText { + Layout.fillWidth: true + wrapMode: Text.Wrap + color: Theme.palette.baseColor1 + text: qsTr("6. Click %1 below").arg(`` + + qsTr("Verify local network access") + + "") + } + } + } + } + footer: StatusDialogFooter { + spacing: Theme.padding + rightButtons: ObjectModel { + StatusFlatButton { + text: qsTr("Cancel") + onClicked: close() + } + StatusButton { + objectName: "btnVerifyNet" + text: loading ? qsTr("Verifying") : qsTr("Verify local network access") + loading: netChecker.checking + interactive: !loading + onClicked: netChecker.checkNetwork() + } + } + } + Connections { + target: netChecker + function onIsOnlineChanged() { + if (netChecker.isOnline) { + root.loginWithSyncingRequested() + close() + } + } + } + } + } }