diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index be1c3b923..8f1c93534 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -129,8 +129,16 @@ file(GLOB_RECURSE PAGE_LOGIC_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/ file(GLOB CONFIGURATORS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/configurators/*.h) file(GLOB CONFIGURATORS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/configurators/*.cpp) -file(GLOB UI_MODELS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/models/*.h ${CMAKE_CURRENT_LIST_DIR}/ui/models/protocols/*.h) -file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/models/*.cpp ${CMAKE_CURRENT_LIST_DIR}/ui/models/protocols/*.cpp) +file(GLOB UI_MODELS_H CONFIGURE_DEPENDS + ${CMAKE_CURRENT_LIST_DIR}/ui/models/*.h + ${CMAKE_CURRENT_LIST_DIR}/ui/models/protocols/*.h + ${CMAKE_CURRENT_LIST_DIR}/ui/models/services/*.h +) +file(GLOB UI_MODELS_CPP CONFIGURE_DEPENDS + ${CMAKE_CURRENT_LIST_DIR}/ui/models/*.cpp + ${CMAKE_CURRENT_LIST_DIR}/ui/models/protocols/*.cpp + ${CMAKE_CURRENT_LIST_DIR}/ui/models/services/*.cpp +) file(GLOB UI_CONTROLLERS_H CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.h) file(GLOB UI_CONTROLLERS_CPP CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ui/controllers/*.cpp) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index abd839edd..7bf035fa3 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -267,6 +267,9 @@ void AmneziaApplication::initModels() m_ikev2ConfigModel.reset(new Ikev2ConfigModel(this)); m_engine->rootContext()->setContextProperty("Ikev2ConfigModel", m_ikev2ConfigModel.get()); #endif + + m_sftpConfigModel.reset(new SftpConfigModel(this)); + m_engine->rootContext()->setContextProperty("SftpConfigModel", m_sftpConfigModel.get()); } void AmneziaApplication::initControllers() diff --git a/client/amnezia_application.h b/client/amnezia_application.h index 6e9fcdf01..3ba42e41a 100644 --- a/client/amnezia_application.h +++ b/client/amnezia_application.h @@ -31,6 +31,7 @@ #include "ui/models/protocols/wireguardConfigModel.h" #include "ui/models/protocols_model.h" #include "ui/models/servers_model.h" +#include "ui/models/services/sftpConfigModel.h" #define amnApp (static_cast(QCoreApplication::instance())) @@ -91,6 +92,8 @@ private: QScopedPointer m_ikev2ConfigModel; #endif + QScopedPointer m_sftpConfigModel; + QSharedPointer m_vpnConnection; QScopedPointer m_notificationHandler; diff --git a/client/images/controls/copy.svg b/client/images/controls/copy.svg new file mode 100644 index 000000000..787e71dbd --- /dev/null +++ b/client/images/controls/copy.svg @@ -0,0 +1,4 @@ + + + + diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index 73c2abdf6..2fba272f7 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -65,6 +65,7 @@ namespace amnezia constexpr char wireguard[] = "wireguard"; constexpr char shadowsocks[] = "shadowsocks"; constexpr char cloak[] = "cloak"; + constexpr char sftp[] = "sftp"; } diff --git a/client/resources.qrc b/client/resources.qrc index 3435a107e..e1afa2944 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -275,5 +275,8 @@ ui/qml/Pages2/PageProtocolCloakSettings.qml ui/qml/Pages2/PageProtocolRaw.qml ui/qml/Pages2/PageSettingsLogging.qml + ui/qml/Pages2/PageServiceSftpSettings.qml + images/controls/copy.svg + ui/qml/Pages2/PageServiceTorWebsiteSettings.qml diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index 2259721a6..84633b54b 100644 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -1,6 +1,8 @@ #include "installController.h" +#include #include +#include #include "core/errorstrings.h" #include "core/servercontroller.h" @@ -214,3 +216,80 @@ void InstallController::setShouldCreateServer(bool shouldCreateServer) { m_shouldCreateServer = shouldCreateServer; } + +void InstallController::mountSftpDrive(const QString &port, const QString &password, const QString &username) +{ + QString mountPath; + QString cmd; + + int serverIndex = m_serversModel->getCurrentlyProcessedServerIndex(); + ServerCredentials serverCredentials = + qvariant_cast(m_serversModel->data(serverIndex, ServersModel::Roles::CredentialsRole)); + QString hostname = serverCredentials.hostName; + +#ifdef Q_OS_WINDOWS + mountPath = getNextDriverLetter() + ":"; + // QString cmd = QString("net use \\\\sshfs\\%1@x.x.x.x!%2 /USER:%1 %3") + // .arg(labelTftpUserNameText()) + // .arg(labelTftpPortText()) + // .arg(labelTftpPasswordText()); + + cmd = "C:\\Program Files\\SSHFS-Win\\bin\\sshfs.exe"; +#elif defined AMNEZIA_DESKTOP + mountPath = + QString("%1/sftp:%2:%3").arg(QStandardPaths::writableLocation(QStandardPaths::HomeLocation), hostname, port); + QDir dir(mountPath); + if (!dir.exists()) { + dir.mkpath(mountPath); + } + + cmd = "/usr/local/bin/sshfs"; +#endif + +#ifdef AMNEZIA_DESKTOP + QSharedPointer process; + process.reset(new QProcess()); + m_sftpMountProcesses.append(process); + process->setProcessChannelMode(QProcess::MergedChannels); + + connect(process.get(), &QProcess::readyRead, this, [this, process, mountPath]() { + QString s = process->readAll(); + if (s.contains("The service sshfs has been started")) { + QDesktopServices::openUrl(QUrl("file:///" + mountPath)); + } + qDebug() << s; + }); + + process->setProgram(cmd); + + QString args = QString("%1@%2:/ %3 " + "-o port=%4 " + "-f " + "-o reconnect " + "-o rellinks " + "-o fstypename=SSHFS " + "-o ssh_command=/usr/bin/ssh.exe " + "-o UserKnownHostsFile=/dev/null " + "-o StrictHostKeyChecking=no " + "-o password_stdin") + .arg(username, hostname, mountPath, port); + + // args.replace("\n", " "); + // args.replace("\r", " "); + // #ifndef Q_OS_WIN + // args.replace("reconnect-orellinks", ""); + // #endif + process->setArguments(args.split(" ", Qt::SkipEmptyParts)); + process->start(); + process->waitForStarted(50); + if (process->state() != QProcess::Running) { + qDebug() << "onPushButtonSftpMountDriveClicked process not started"; + qDebug() << args; + } else { + process->write((password + "\n").toUtf8()); + } + + // qDebug().noquote() << "onPushButtonSftpMountDriveClicked" << args; + +#endif +} diff --git a/client/ui/controllers/installController.h b/client/ui/controllers/installController.h index 75f4fdefb..8458b46d7 100644 --- a/client/ui/controllers/installController.h +++ b/client/ui/controllers/installController.h @@ -2,6 +2,7 @@ #define INSTALLCONTROLLER_H #include +#include #include "containers/containers_defs.h" #include "core/defs.h" @@ -28,6 +29,8 @@ public slots: QRegularExpression ipAddressPortRegExp(); + void mountSftpDrive(const QString &port, const QString &password, const QString &username); + signals: void installContainerFinished(bool isInstalledContainerFound); void installServerFinished(bool isInstalledContainerFound); @@ -52,6 +55,8 @@ private: ServerCredentials m_currentlyInstalledServerCredentials; bool m_shouldCreateServer; + + QList> m_sftpMountProcesses; }; #endif // INSTALLCONTROLLER_H diff --git a/client/ui/controllers/pageController.h b/client/ui/controllers/pageController.h index a0b9753b8..d79b100a6 100644 --- a/client/ui/controllers/pageController.h +++ b/client/ui/controllers/pageController.h @@ -29,6 +29,9 @@ namespace PageLoader PageSettingsAbout, PageSettingsLogging, + PageServiceSftpSettings, + PageServiceTorWebsiteSettings, + PageSetupWizardStart, PageSetupWizardCredentials, PageSetupWizardProtocols, diff --git a/client/ui/models/containers_model.cpp b/client/ui/models/containers_model.cpp index 7b6ffaeea..922542dd4 100644 --- a/client/ui/models/containers_model.cpp +++ b/client/ui/models/containers_model.cpp @@ -118,6 +118,11 @@ QString ContainersModel::getCurrentlyProcessedContainerName() return ContainerProps::containerHumanNames().value(static_cast(m_currentlyProcessedContainerIndex)); } +QJsonObject ContainersModel::getCurrentlyProcessedContainerConfig() +{ + return qvariant_cast(data(index(m_currentlyProcessedContainerIndex), ConfigRole)); +} + void ContainersModel::removeAllContainers() { diff --git a/client/ui/models/containers_model.h b/client/ui/models/containers_model.h index b79b058ac..75ba91142 100644 --- a/client/ui/models/containers_model.h +++ b/client/ui/models/containers_model.h @@ -50,6 +50,7 @@ public slots: int getCurrentlyProcessedContainerIndex(); QString getCurrentlyProcessedContainerName(); + QJsonObject getCurrentlyProcessedContainerConfig(); void removeAllContainers(); void removeCurrentlyProcessedContainer(); diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index 6fad9af66..fabbb8296 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -106,6 +106,11 @@ int ServersModel::getCurrentlyProcessedServerIndex() return m_currentlyProcessedServerIndex; } +QString ServersModel::getCurrentlyProcessedServerHostName() +{ + return qvariant_cast(data(m_currentlyProcessedServerIndex, HostNameRole)); +} + bool ServersModel::isDefaultServerCurrentlyProcessed() { return m_defaultServerIndex == m_currentlyProcessedServerIndex; diff --git a/client/ui/models/servers_model.h b/client/ui/models/servers_model.h index f149d85b3..a31e3c044 100644 --- a/client/ui/models/servers_model.h +++ b/client/ui/models/servers_model.h @@ -45,6 +45,8 @@ public slots: void setCurrentlyProcessedServerIndex(const int index); int getCurrentlyProcessedServerIndex(); + QString getCurrentlyProcessedServerHostName(); + void addServer(const QJsonObject &server); void removeServer(); diff --git a/client/ui/models/services/sftpConfigModel.cpp b/client/ui/models/services/sftpConfigModel.cpp new file mode 100644 index 000000000..3cbb5ebcd --- /dev/null +++ b/client/ui/models/services/sftpConfigModel.cpp @@ -0,0 +1,64 @@ +#include "sftpConfigModel.h" + +#include "protocols/protocols_defs.h" + +SftpConfigModel::SftpConfigModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +int SftpConfigModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 1; +} + +QVariant SftpConfigModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) { + return false; + } + + switch (role) { + case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(); + case Roles::UserNameRole: + return m_protocolConfig.value(config_key::userName).toString(protocols::sftp::defaultUserName); + case Roles::PasswordRole: return m_protocolConfig.value(config_key::password).toString(); + } + + return QVariant(); +} + +void SftpConfigModel::updateModel(const QJsonObject &config) +{ + beginResetModel(); + m_container = ContainerProps::containerFromString(config.value(config_key::container).toString()); + + m_fullConfig = config; + QJsonObject protocolConfig = config.value(config_key::sftp).toObject(); + + m_protocolConfig.insert(config_key::userName, + protocolConfig.value(config_key::userName).toString(protocols::sftp::defaultUserName)); + + m_protocolConfig.insert(config_key::password, protocolConfig.value(config_key::password).toString()); + + m_protocolConfig.insert(config_key::port, protocolConfig.value(config_key::port).toString()); + + endResetModel(); +} + +QJsonObject SftpConfigModel::getConfig() +{ + m_fullConfig.insert(config_key::sftp, m_protocolConfig); + return m_fullConfig; +} + +QHash SftpConfigModel::roleNames() const +{ + QHash roles; + + roles[PortRole] = "port"; + roles[UserNameRole] = "username"; + roles[PasswordRole] = "password"; + + return roles; +} diff --git a/client/ui/models/services/sftpConfigModel.h b/client/ui/models/services/sftpConfigModel.h new file mode 100644 index 000000000..e948591e5 --- /dev/null +++ b/client/ui/models/services/sftpConfigModel.h @@ -0,0 +1,39 @@ +#ifndef SFTPCONFIGMODEL_H +#define SFTPCONFIGMODEL_H + +#include +#include + +#include "containers/containers_defs.h" + +class SftpConfigModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles { + PortRole = Qt::UserRole + 1, + UserNameRole, + PasswordRole + }; + + explicit SftpConfigModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + +public slots: + void updateModel(const QJsonObject &config); + QJsonObject getConfig(); + +protected: + QHash roleNames() const override; + +private: + DockerContainer m_container; + QJsonObject m_protocolConfig; + QJsonObject m_fullConfig; +}; + +#endif // SFTPCONFIGMODEL_H diff --git a/client/ui/qml/Components/SettingsContainersListView.qml b/client/ui/qml/Components/SettingsContainersListView.qml index d2f3ee81a..eac473f46 100644 --- a/client/ui/qml/Components/SettingsContainersListView.qml +++ b/client/ui/qml/Components/SettingsContainersListView.qml @@ -92,6 +92,7 @@ ListView { if (isInstalled) { var containerIndex = root.model.mapToSource(index) ContainersModel.setCurrentlyProcessedContainerIndex(containerIndex) + if (config[ContainerProps.containerTypeToString(containerIndex)]["isThirdPartyConfig"]) { ProtocolsModel.updateModel(config) goToPage(PageEnum.PageProtocolRaw) @@ -114,6 +115,16 @@ ListView { goToPage(PageEnum.PageProtocolIKev2Settings) break } + case ContainerEnum.Sftp: { + SftpConfigModel.updateModel(config) + goToPage(PageEnum.PageServiceSftpSettings) + break + } + case ContainerEnum.TorWebSite: { + goToPage(PageEnum.PageServiceTorWebsiteSettings) + break + } + default: { if (serviceType !== ProtocolEnum.Other) { //todo disable settings for dns container ProtocolsModel.updateModel(config) diff --git a/client/ui/qml/Controls2/LabelWithButtonType.qml b/client/ui/qml/Controls2/LabelWithButtonType.qml index c925f8019..3aca1d57b 100644 --- a/client/ui/qml/Controls2/LabelWithButtonType.qml +++ b/client/ui/qml/Controls2/LabelWithButtonType.qml @@ -16,6 +16,10 @@ Item { property string leftImageSource property string textColor: "#d7d8db" + property string descriptionColor: "#878B91" + property string rightImageColor: "#878B91" + + property bool descriptionOnTop: false implicitWidth: content.implicitWidth + content.anchors.topMargin + content.anchors.bottomMargin implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin @@ -53,26 +57,41 @@ Item { } ColumnLayout { + property real textLineHeight: 21.6 + property real descriptionTextLineHeight: 16 + + property int textPixelSize: 18 + property int descriptionTextSize: 13 + ListItemTitleType { text: root.text - color: root.textColor + color: root.descriptionOnTop ? root.descriptionColor : root.textColor Layout.fillWidth: true + lineHeight: root.descriptionOnTop ? parent.descriptionTextLineHeight : parent.textLineHeight + font.pixelSize: root.descriptionOnTop ? parent.descriptionTextSize : parent.textPixelSize + font.letterSpacing: root.descriptionOnTop ? 0.02 : 0 + horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter } + CaptionTextType { id: description - color: "#878B91" + color: root.descriptionOnTop ? root.textColor : root.descriptionColor text: root.descriptionText visible: root.descriptionText !== "" Layout.fillWidth: true + lineHeight: root.descriptionOnTop ? parent.textLineHeight : parent.descriptionTextLineHeight + font.pixelSize: root.descriptionOnTop ? parent.textPixelSize : parent.descriptionTextSize + font.letterSpacing: root.descriptionOnTop ? 0 : 0.02 + horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter } @@ -83,6 +102,7 @@ Item { hoverEnabled: false image: rightImageSource + imageColor: rightImageColor visible: rightImageSource ? true : false Layout.alignment: Qt.AlignRight diff --git a/client/ui/qml/Controls2/TextTypes/CaptionTextType.qml b/client/ui/qml/Controls2/TextTypes/CaptionTextType.qml index b9e41da2d..f7acb6dde 100644 --- a/client/ui/qml/Controls2/TextTypes/CaptionTextType.qml +++ b/client/ui/qml/Controls2/TextTypes/CaptionTextType.qml @@ -10,5 +10,5 @@ Text { font.family: "PT Root UI VF" font.letterSpacing: 0.02 - wrapMode: Text.WordWrap + wrapMode: Text.Wrap } diff --git a/client/ui/qml/Controls2/TextTypes/ListItemTitleType.qml b/client/ui/qml/Controls2/TextTypes/ListItemTitleType.qml index 30bd7900d..917e65def 100644 --- a/client/ui/qml/Controls2/TextTypes/ListItemTitleType.qml +++ b/client/ui/qml/Controls2/TextTypes/ListItemTitleType.qml @@ -9,5 +9,5 @@ Text { font.weight: 400 font.family: "PT Root UI VF" - wrapMode: Text.WordWrap + wrapMode: Text.Wrap } diff --git a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml index 0bad68e94..028f9fd35 100644 --- a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml @@ -413,6 +413,8 @@ PageType { BasicButtonType { Layout.topMargin: 24 + Layout.leftMargin: -8 + implicitHeight: 32 defaultColor: "transparent" hoveredColor: Qt.rgba(1, 1, 1, 0.08) diff --git a/client/ui/qml/Pages2/PageServiceSftpSettings.qml b/client/ui/qml/Pages2/PageServiceSftpSettings.qml new file mode 100644 index 000000000..ffa428591 --- /dev/null +++ b/client/ui/qml/Pages2/PageServiceSftpSettings.qml @@ -0,0 +1,280 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import SortFilterProxyModel 0.2 + +import PageEnum 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +PageType { + id: root + + //todo move to main? + Connections { + target: InstallController + + function onInstallationErrorOccurred(errorMessage) { + PageController.showErrorMessage(errorMessage) + } + + function onUpdateContainerFinished() { + //todo change to notification + PageController.showErrorMessage(qsTr("Settings updated successfully")) + } + } + + ColumnLayout { + id: backButton + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.topMargin: 20 + + BackButtonType { + } + } + + FlickableType { + id: fl + anchors.top: backButton.bottom + anchors.bottom: parent.bottom + contentHeight: content.implicitHeight + + Column { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + enabled: ServersModel.isCurrentlyProcessedServerHasWriteAccess() + + ListView { + id: listview + + width: parent.width + height: listview.contentItem.height + + clip: true + interactive: false + + model: SftpConfigModel + + delegate: Item { + implicitWidth: listview.width + implicitHeight: col.implicitHeight + + ColumnLayout { + id: col + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + spacing: 0 + + HeaderType { + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + headerText: qsTr("SFTP settings") + } + + LabelWithButtonType { + Layout.fillWidth: true + Layout.topMargin: 32 + + text: qsTr("Host") + descriptionText: ServersModel.getCurrentlyProcessedServerHostName() + + descriptionOnTop: true + + rightImageSource: "qrc:/images/controls/copy.svg" + rightImageColor: "#D7D8DB" + + clickedFunction: function() { + col.copyToClipBoard(descriptionText) + } + } + + LabelWithButtonType { + Layout.fillWidth: true + + text: qsTr("Port") + descriptionText: port + + descriptionOnTop: true + + rightImageSource: "qrc:/images/controls/copy.svg" + rightImageColor: "#D7D8DB" + + clickedFunction: function() { + col.copyToClipBoard(descriptionText) + } + } + + LabelWithButtonType { + Layout.fillWidth: true + + text: qsTr("Login") + descriptionText: username + + descriptionOnTop: true + + rightImageSource: "qrc:/images/controls/copy.svg" + rightImageColor: "#D7D8DB" + + clickedFunction: function() { + col.copyToClipBoard(descriptionText) + } + } + + LabelWithButtonType { + Layout.fillWidth: true + + text: qsTr("Password") + descriptionText: password + + descriptionOnTop: true + + rightImageSource: "qrc:/images/controls/copy.svg" + rightImageColor: "#D7D8DB" + + clickedFunction: function() { + col.copyToClipBoard(descriptionText) + } + } + + TextEdit{ + id: clipboard + visible: false + } + + function copyToClipBoard(text) { + clipboard.text = text + clipboard.selectAll() + clipboard.copy() + clipboard.select(0, 0) + } + + BasicButtonType { + Layout.fillWidth: true + Layout.topMargin: 24 + Layout.bottomMargin: 24 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + defaultColor: "transparent" + hoveredColor: Qt.rgba(1, 1, 1, 0.08) + pressedColor: Qt.rgba(1, 1, 1, 0.12) + disabledColor: "#878B91" + textColor: "#D7D8DB" + borderWidth: 1 + + text: qsTr("Mount folder on device") + + onClicked: { + PageController.showBusyIndicator(true) + InstallController.mountSftpDrive(port, password, username) + PageController.showBusyIndicator(false) + } + } + + ParagraphTextType { + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + readonly property string windowsFirstLink: "WinFsp" + readonly property string windowsSecondLink: "SSHFS-Win" + + readonly property string macosFirstLink: "macFUSE" + readonly property string macosSecondLink: "SSHFS" + + onLinkActivated: Qt.openUrlExternally(link) + textFormat: Text.RichText + text: { + var str = qsTr("In order to mount remote SFTP folder as local drive, perform following steps:
") + if (Qt.platform.os === "windows") { + str += qsTr("
1. Install the latest version of ") + windowsFirstLink + "\n" + str += qsTr("
2. Install the latest version of ") + windowsSecondLink + "\n" + } else if (Qt.platform.os === "osx") { + str += qsTr("
1. Install the latest version of ") + macosFirstLink + "\n" + str += qsTr("
2. Install the latest version of ") + macosSecondLink + "\n" + } else if (Qt.platform.os === "linux") { + return "" + } else return "" + + return str + } + } + + BasicButtonType { + Layout.topMargin: 16 + Layout.bottomMargin: 16 + Layout.leftMargin: 8 + implicitHeight: 32 + + defaultColor: "transparent" + hoveredColor: Qt.rgba(1, 1, 1, 0.08) + pressedColor: Qt.rgba(1, 1, 1, 0.12) + disabledColor: "#878B91" + textColor: "#FBB26A" + + text: qsTr("Detailed instructions") + + onClicked: { +// Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest") + } + } + + BasicButtonType { + Layout.topMargin: 24 + Layout.bottomMargin: 16 + Layout.leftMargin: 8 + implicitHeight: 32 + + defaultColor: "transparent" + hoveredColor: Qt.rgba(1, 1, 1, 0.08) + pressedColor: Qt.rgba(1, 1, 1, 0.12) + textColor: "#EB5757" + + text: qsTr("Remove SFTP and all data stored there") + + onClicked: { + questionDrawer.headerText = qsTr("Some description") + questionDrawer.yesButtonText = qsTr("Continue") + questionDrawer.noButtonText = qsTr("Cancel") + + questionDrawer.yesButtonFunction = function() { + questionDrawer.visible = false + goToPage(PageEnum.PageDeinstalling) + ContainersModel.removeCurrentlyProcessedContainer() + closePage() + closePage() //todo auto close to deinstall page? + } + questionDrawer.noButtonFunction = function() { + questionDrawer.visible = false + } + questionDrawer.visible = true + } + } + } + } + } + } + + QuestionDrawer { + id: questionDrawer + } + } +} diff --git a/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml b/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml new file mode 100644 index 000000000..a47a95e3a --- /dev/null +++ b/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml @@ -0,0 +1,169 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import SortFilterProxyModel 0.2 + +import PageEnum 1.0 +import ContainerProps 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +PageType { + id: root + + //todo move to main? + Connections { + target: InstallController + + function onInstallationErrorOccurred(errorMessage) { + PageController.showErrorMessage(errorMessage) + } + + function onUpdateContainerFinished() { + //todo change to notification + PageController.showErrorMessage(qsTr("Settings updated successfully")) + } + } + + ColumnLayout { + id: backButton + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.topMargin: 20 + + BackButtonType { + } + } + + FlickableType { + id: fl + anchors.top: backButton.bottom + anchors.bottom: parent.bottom + contentHeight: content.implicitHeight + + ColumnLayout { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + spacing: 0 + + HeaderType { + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + headerText: qsTr("Tor website settings") + } + + LabelWithButtonType { + Layout.fillWidth: true + Layout.topMargin: 32 + + text: qsTr("Website address") + descriptionText: { + var config = ContainersModel.getCurrentlyProcessedContainerConfig() + var containerIndex = ContainersModel.getCurrentlyProcessedContainerIndex() + return config[ContainerProps.containerTypeToString(containerIndex)]["site"] + } + + descriptionOnTop: true + textColor: "#FBB26A" + + rightImageSource: "qrc:/images/controls/copy.svg" + rightImageColor: "#D7D8DB" + + clickedFunction: function() { + content.copyToClipBoard(descriptionText) + } + } + + TextEdit{ + id: clipboard + visible: false + } + + function copyToClipBoard(text) { + clipboard.text = text + clipboard.selectAll() + clipboard.copy() + clipboard.select(0, 0) + } + + ParagraphTextType { + Layout.fillWidth: true + Layout.topMargin: 40 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + onLinkActivated: Qt.openUrlExternally(link) + textFormat: Text.RichText + text: qsTr("Use Tor Browser to open this url.") + } + + ParagraphTextType { + Layout.fillWidth: true + Layout.topMargin: 16 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + text: qsTr("After installation it takes several minutes while your onion site will become available in the Tor Network.") + } + + ParagraphTextType { + Layout.fillWidth: true + Layout.topMargin: 16 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + text: qsTr("When configuring WordPress set the domain as this onion address.") + } + + BasicButtonType { + Layout.topMargin: 24 + Layout.bottomMargin: 16 + Layout.leftMargin: 8 + implicitHeight: 32 + + defaultColor: "transparent" + hoveredColor: Qt.rgba(1, 1, 1, 0.08) + pressedColor: Qt.rgba(1, 1, 1, 0.12) + textColor: "#EB5757" + + text: qsTr("Remove website") + + onClicked: { + questionDrawer.headerText = qsTr("Some description") + questionDrawer.yesButtonText = qsTr("Continue") + questionDrawer.noButtonText = qsTr("Cancel") + + questionDrawer.yesButtonFunction = function() { + questionDrawer.visible = false + goToPage(PageEnum.PageDeinstalling) + ContainersModel.removeCurrentlyProcessedContainer() + closePage() + closePage() //todo auto close to deinstall page? + } + questionDrawer.noButtonFunction = function() { + questionDrawer.visible = false + } + questionDrawer.visible = true + } + } + } + + QuestionDrawer { + id: questionDrawer + } + } +}