mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
feat: add new premium info page
This commit is contained in:
6
client/images/controls/globe-2.svg
Normal file
6
client/images/controls/globe-2.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15 21V17C15 16.4696 15.2107 15.9609 15.5858 15.5858C15.9609 15.2107 16.4696 15 17 15H21" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7 4V6C7.21572 6.61347 7.62494 7.14024 8.16602 7.50096C8.7071 7.86168 9.35075 8.03682 10 8V8C10.5304 8 11.0391 8.21071 11.4142 8.58579C11.7893 8.96086 12 9.46957 12 10C12 10.5304 12.2107 11.0391 12.5858 11.4142C12.9609 11.7893 13.4696 12 14 12C14.5304 12 15.0391 11.7893 15.4142 11.4142C15.7893 11.0391 16 10.5304 16 10C16 9.46957 16.2107 8.96086 16.5858 8.58579C16.9609 8.21071 17.4696 8 18 8H21" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3 11H5C5.53043 11 6.03914 11.2107 6.41421 11.5858C6.78929 11.9609 7 12.4696 7 13V14C7 14.5304 7.21071 15.0391 7.58579 15.4142C7.96086 15.7893 8.46957 16 9 16C9.53043 16 10.0391 16.2107 10.4142 16.5858C10.7893 16.9609 11 17.4696 11 18V22" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
3
client/images/controls/infinity.svg
Normal file
3
client/images/controls/infinity.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18.1777 8C23.2737 8 23.2737 16 18.1777 16C13.0827 16 11.0447 8 5.43875 8C0.85375 8 0.85375 16 5.43875 16C11.0447 16 13.0828 8 18.1788 8H18.1777Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 342 B |
4
client/images/controls/smartphone.svg
Normal file
4
client/images/controls/smartphone.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17 2H7C5.89543 2 5 2.89543 5 4V20C5 21.1046 5.89543 22 7 22H17C18.1046 22 19 21.1046 19 20V4C19 2.89543 18.1046 2 17 2Z" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 18H12.01" stroke="#D7D8DB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 423 B |
@@ -27,10 +27,12 @@
|
||||
<file>images/controls/folder-open.svg</file>
|
||||
<file>images/controls/folder-search-2.svg</file>
|
||||
<file>images/controls/gauge.svg</file>
|
||||
<file>images/controls/globe-2.svg</file>
|
||||
<file>images/controls/github.svg</file>
|
||||
<file>images/controls/help-circle.svg</file>
|
||||
<file>images/controls/history.svg</file>
|
||||
<file>images/controls/home.svg</file>
|
||||
<file>images/controls/infinity.svg</file>
|
||||
<file>images/controls/info.svg</file>
|
||||
<file>images/controls/mail.svg</file>
|
||||
<file>images/controls/map-pin.svg</file>
|
||||
@@ -55,6 +57,7 @@
|
||||
<file>images/controls/settings-news.svg</file>
|
||||
<file>images/controls/share-2.svg</file>
|
||||
<file>images/controls/split-tunneling.svg</file>
|
||||
<file>images/controls/smartphone.svg</file>
|
||||
<file>images/controls/tag.svg</file>
|
||||
<file>images/controls/telegram.svg</file>
|
||||
<file>images/controls/text-cursor.svg</file>
|
||||
@@ -133,6 +136,9 @@
|
||||
<file>ui/qml/Components/HomeContainersListView.qml</file>
|
||||
<file>ui/qml/Components/HomeSplitTunnelingDrawer.qml</file>
|
||||
<file>ui/qml/Components/InstalledAppsDrawer.qml</file>
|
||||
<file>ui/qml/Components/BenefitRow.qml</file>
|
||||
<file>ui/qml/Components/BenefitsPanel.qml</file>
|
||||
<file>ui/qml/Components/SubscriptionPlanCard.qml</file>
|
||||
<file>ui/qml/Components/QuestionDrawer.qml</file>
|
||||
<file>ui/qml/Components/SelectLanguageDrawer.qml</file>
|
||||
<file>ui/qml/Components/SubscriptionExpiredDrawer.qml</file>
|
||||
@@ -227,6 +233,7 @@
|
||||
<file>ui/qml/Pages2/PageProtocolAwgClientSettings.qml</file>
|
||||
<file>ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml</file>
|
||||
<file>ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml</file>
|
||||
<file>ui/qml/Pages2/PageSetupWizardApiPremium.qml</file>
|
||||
<file>ui/qml/Pages2/PageSetupWizardApiServicesList.qml</file>
|
||||
<file>ui/qml/Pages2/PageSetupWizardConfigSource.qml</file>
|
||||
<file>ui/qml/Pages2/PageSetupWizardCredentials.qml</file>
|
||||
|
||||
@@ -76,6 +76,8 @@ namespace PageLoader
|
||||
PageShareFullAccess,
|
||||
PageShareConnection,
|
||||
|
||||
PageSetupWizardApiPremiumInfo,
|
||||
|
||||
PageDevMenu
|
||||
};
|
||||
Q_ENUM_NS(PageEnum)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "apiServicesModel.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "core/api/apiDefs.h"
|
||||
@@ -34,6 +35,30 @@ namespace
|
||||
constexpr char storeEndpoint[] = "store_endpoint";
|
||||
|
||||
constexpr char isAvailable[] = "is_available";
|
||||
|
||||
constexpr char subscriptionPlans[] = "subscription_plans";
|
||||
|
||||
constexpr char premiumBenefitCountriesTitle[] = "premium_benefit_countries_title";
|
||||
constexpr char premiumBenefitCountriesBody[] = "premium_benefit_countries_body";
|
||||
constexpr char premiumBenefitDevicesTitle[] = "premium_benefit_devices_title";
|
||||
constexpr char premiumBenefitDevicesBody[] = "premium_benefit_devices_body";
|
||||
constexpr char premiumBenefitVideoTitle[] = "premium_benefit_video_title";
|
||||
constexpr char premiumBenefitVideoBody[] = "premium_benefit_video_body";
|
||||
constexpr char premiumBenefitTrafficTitle[] = "premium_benefit_traffic_title";
|
||||
constexpr char premiumBenefitTrafficBody[] = "premium_benefit_traffic_body";
|
||||
}
|
||||
|
||||
QVariantList jsonObjectArrayToVariantList(const QJsonArray &arr)
|
||||
{
|
||||
QVariantList list;
|
||||
list.reserve(arr.size());
|
||||
for (const QJsonValue &v : arr) {
|
||||
if (!v.isObject()) {
|
||||
continue;
|
||||
}
|
||||
list.append(v.toObject().toVariantMap());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
namespace serviceType
|
||||
@@ -124,11 +149,20 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
|
||||
case OrderRole: {
|
||||
if (serviceType == serviceType::amneziaPremium) {
|
||||
return 0;
|
||||
} else if (serviceType == serviceType::amneziaTrial) {
|
||||
}
|
||||
if (serviceType == serviceType::amneziaTrial) {
|
||||
return 1;
|
||||
} else if (serviceType == serviceType::amneziaFree) {
|
||||
}
|
||||
if (serviceType == serviceType::amneziaFree) {
|
||||
return 2;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
case SubscriptionPlansRole: {
|
||||
return apiServiceData.subscriptionPlans;
|
||||
}
|
||||
case PremiumBenefitPanelRowsRole: {
|
||||
return ApiServicesModel::buildPremiumBenefitPanelRows(apiServiceData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,6 +250,32 @@ QVariant ApiServicesModel::getSelectedServiceData(const QString roleString)
|
||||
return {};
|
||||
}
|
||||
|
||||
int ApiServicesModel::serviceIndexForType(const QString &type) const
|
||||
{
|
||||
for (int i = 0; i < m_services.size(); ++i) {
|
||||
if (m_services.at(i).type == type) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
QVariant ApiServicesModel::getServiceFieldForType(const QString &type, const QString &roleString) const
|
||||
{
|
||||
const int row = serviceIndexForType(type);
|
||||
if (row < 0) {
|
||||
return {};
|
||||
}
|
||||
const QModelIndex modelIndex = index(row);
|
||||
const auto roles = roleNames();
|
||||
for (auto it = roles.begin(); it != roles.end(); ++it) {
|
||||
if (QString(it.value()) == roleString) {
|
||||
return data(modelIndex, it.key());
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> ApiServicesModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
@@ -230,6 +290,8 @@ QHash<int, QByteArray> ApiServicesModel::roleNames() const
|
||||
roles[PriceRole] = "price";
|
||||
roles[EndDateRole] = "endDate";
|
||||
roles[OrderRole] = "order";
|
||||
roles[SubscriptionPlansRole] = "subscriptionPlans";
|
||||
roles[PremiumBenefitPanelRowsRole] = "premiumBenefitRows";
|
||||
|
||||
return roles;
|
||||
}
|
||||
@@ -255,6 +317,17 @@ ApiServicesModel::ApiServicesData ApiServicesModel::getApiServicesData(const QJs
|
||||
serviceData.serviceInfo.description = serviceDescription.value(configKey::description).toString();
|
||||
serviceData.serviceInfo.features = serviceDescription.value(configKey::features).toString();
|
||||
|
||||
serviceData.subscriptionPlans = jsonObjectArrayToVariantList(serviceDescription.value(configKey::subscriptionPlans).toArray());
|
||||
|
||||
serviceData.premiumBenefitCountriesTitle = serviceDescription.value(configKey::premiumBenefitCountriesTitle).toString();
|
||||
serviceData.premiumBenefitCountriesBody = serviceDescription.value(configKey::premiumBenefitCountriesBody).toString();
|
||||
serviceData.premiumBenefitDevicesTitle = serviceDescription.value(configKey::premiumBenefitDevicesTitle).toString();
|
||||
serviceData.premiumBenefitDevicesBody = serviceDescription.value(configKey::premiumBenefitDevicesBody).toString();
|
||||
serviceData.premiumBenefitVideoTitle = serviceDescription.value(configKey::premiumBenefitVideoTitle).toString();
|
||||
serviceData.premiumBenefitVideoBody = serviceDescription.value(configKey::premiumBenefitVideoBody).toString();
|
||||
serviceData.premiumBenefitTrafficTitle = serviceDescription.value(configKey::premiumBenefitTrafficTitle).toString();
|
||||
serviceData.premiumBenefitTrafficBody = serviceDescription.value(configKey::premiumBenefitTrafficBody).toString();
|
||||
|
||||
serviceData.type = serviceType;
|
||||
serviceData.protocol = serviceProtocol;
|
||||
|
||||
@@ -273,3 +346,30 @@ ApiServicesModel::ApiServicesData ApiServicesModel::getApiServicesData(const QJs
|
||||
|
||||
return serviceData;
|
||||
}
|
||||
|
||||
QVariantList ApiServicesModel::buildPremiumBenefitPanelRows(const ApiServicesData &service)
|
||||
{
|
||||
const QStringList icons = { QStringLiteral("qrc:/images/controls/globe-2.svg"),
|
||||
QStringLiteral("qrc:/images/controls/smartphone.svg"),
|
||||
QStringLiteral("qrc:/images/controls/gauge.svg"),
|
||||
QStringLiteral("qrc:/images/controls/infinity.svg") };
|
||||
|
||||
QVariantList out;
|
||||
|
||||
const auto appendRow = [&out, &icons](int iconIndex, const QString &title, const QString &body) {
|
||||
if (title.isEmpty() && body.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
QVariantMap m;
|
||||
m.insert(QStringLiteral("icon"), icons.at(iconIndex));
|
||||
m.insert(QStringLiteral("title"), title);
|
||||
m.insert(QStringLiteral("body"), body);
|
||||
out.append(m);
|
||||
};
|
||||
|
||||
appendRow(0, service.premiumBenefitCountriesTitle, service.premiumBenefitCountriesBody);
|
||||
appendRow(1, service.premiumBenefitDevicesTitle, service.premiumBenefitDevicesBody);
|
||||
appendRow(2, service.premiumBenefitVideoTitle, service.premiumBenefitVideoBody);
|
||||
appendRow(3, service.premiumBenefitTrafficTitle, service.premiumBenefitTrafficBody);
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <QAbstractListModel>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QVariantList>
|
||||
|
||||
class ApiServicesModel : public QAbstractListModel
|
||||
{
|
||||
@@ -21,7 +22,9 @@ public:
|
||||
FeaturesRole,
|
||||
PriceRole,
|
||||
EndDateRole,
|
||||
OrderRole
|
||||
OrderRole,
|
||||
SubscriptionPlansRole,
|
||||
PremiumBenefitPanelRowsRole
|
||||
};
|
||||
|
||||
explicit ApiServicesModel(QObject *parent = nullptr);
|
||||
@@ -47,6 +50,9 @@ public slots:
|
||||
|
||||
QVariant getSelectedServiceData(const QString roleString);
|
||||
|
||||
Q_INVOKABLE int serviceIndexForType(const QString &type) const;
|
||||
Q_INVOKABLE QVariant getServiceFieldForType(const QString &type, const QString &roleString) const;
|
||||
|
||||
protected:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
@@ -83,10 +89,22 @@ private:
|
||||
Subscription subscription;
|
||||
|
||||
QJsonArray availableCountries;
|
||||
|
||||
QVariantList subscriptionPlans;
|
||||
QString premiumBenefitCountriesTitle;
|
||||
QString premiumBenefitCountriesBody;
|
||||
QString premiumBenefitDevicesTitle;
|
||||
QString premiumBenefitDevicesBody;
|
||||
QString premiumBenefitVideoTitle;
|
||||
QString premiumBenefitVideoBody;
|
||||
QString premiumBenefitTrafficTitle;
|
||||
QString premiumBenefitTrafficBody;
|
||||
};
|
||||
|
||||
ApiServicesData getApiServicesData(const QJsonObject &data);
|
||||
|
||||
static QVariantList buildPremiumBenefitPanelRows(const ApiServicesData &service);
|
||||
|
||||
QString m_countryCode;
|
||||
QVector<ApiServicesData> m_services;
|
||||
|
||||
|
||||
46
client/ui/qml/Components/BenefitRow.qml
Normal file
46
client/ui/qml/Components/BenefitRow.qml
Normal file
@@ -0,0 +1,46 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "../Controls2/TextTypes"
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
|
||||
property string iconSource: ""
|
||||
property string titleText: ""
|
||||
property string bodyText: ""
|
||||
|
||||
spacing: 12
|
||||
|
||||
Image {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.preferredWidth: 22
|
||||
Layout.preferredHeight: 22
|
||||
source: root.iconSource
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 4
|
||||
|
||||
LabelTextType {
|
||||
Layout.fillWidth: true
|
||||
text: root.titleText
|
||||
color: AmneziaStyle.color.paleGray
|
||||
font.pixelSize: 16
|
||||
font.weight: Font.DemiBold
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
LabelTextType {
|
||||
Layout.fillWidth: true
|
||||
text: root.bodyText
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
font.pixelSize: 14
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
}
|
||||
}
|
||||
37
client/ui/qml/Components/BenefitsPanel.qml
Normal file
37
client/ui/qml/Components/BenefitsPanel.qml
Normal file
@@ -0,0 +1,37 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import "."
|
||||
|
||||
import Style 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property var benefitItems: []
|
||||
|
||||
radius: 16
|
||||
color: "#1C1C1E"
|
||||
implicitHeight: inner.implicitHeight + 24
|
||||
|
||||
ColumnLayout {
|
||||
id: inner
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.margins: 12
|
||||
spacing: 20
|
||||
|
||||
Repeater {
|
||||
model: root.benefitItems ? root.benefitItems.length : 0
|
||||
|
||||
delegate: BenefitRow {
|
||||
Layout.fillWidth: true
|
||||
iconSource: root.benefitItems[index].icon
|
||||
titleText: root.benefitItems[index].title
|
||||
bodyText: root.benefitItems[index].body
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
94
client/ui/qml/Components/SubscriptionPlanCard.qml
Normal file
94
client/ui/qml/Components/SubscriptionPlanCard.qml
Normal file
@@ -0,0 +1,94 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "../Controls2/TextTypes"
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool selected: false
|
||||
property string primaryLeft: ""
|
||||
property string primaryRight: ""
|
||||
property string subtitle: ""
|
||||
property bool showRecommendedBadge: false
|
||||
property string recommendedText: "Recommended"
|
||||
|
||||
signal selectRequested
|
||||
|
||||
implicitHeight: cardLayout.implicitHeight + 28
|
||||
radius: 16
|
||||
color: AmneziaStyle.color.transparent
|
||||
border.width: selected ? 2 : 1
|
||||
border.color: selected ? AmneziaStyle.color.goldenApricot : AmneziaStyle.color.charcoalGray
|
||||
|
||||
ColumnLayout {
|
||||
id: cardLayout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
spacing: 8
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
LabelTextType {
|
||||
Layout.fillWidth: true
|
||||
text: root.primaryLeft
|
||||
color: root.selected ? AmneziaStyle.color.goldenApricot : AmneziaStyle.color.paleGray
|
||||
font.pixelSize: 17
|
||||
font.weight: Font.DemiBold
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
LabelTextType {
|
||||
text: root.primaryRight
|
||||
color: root.selected ? AmneziaStyle.color.goldenApricot : AmneziaStyle.color.paleGray
|
||||
font.pixelSize: 17
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
visible: root.subtitle.length > 0 || root.showRecommendedBadge
|
||||
|
||||
LabelTextType {
|
||||
Layout.fillWidth: true
|
||||
text: root.subtitle
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
font.pixelSize: 13
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: root.showRecommendedBadge
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
radius: 10
|
||||
color: AmneziaStyle.color.softViolet
|
||||
implicitHeight: recLabel.implicitHeight + 8
|
||||
implicitWidth: recLabel.implicitWidth + 16
|
||||
|
||||
LabelTextType {
|
||||
id: recLabel
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: root.recommendedText
|
||||
color: AmneziaStyle.color.midnightBlack
|
||||
font.pixelSize: 11
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: root.selectRequested()
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ QtObject {
|
||||
readonly property color onyxBlack: '#1C1D21'
|
||||
readonly property color midnightBlack: '#0E0E11'
|
||||
readonly property color goldenApricot: '#FBB26A'
|
||||
readonly property color softViolet: '#A87BE2'
|
||||
readonly property color burntOrange: '#A85809'
|
||||
readonly property color mutedBrown: '#84603D'
|
||||
readonly property color richBrown: '#633303'
|
||||
|
||||
297
client/ui/qml/Pages2/PageSetupWizardApiPremiumInfo.qml
Normal file
297
client/ui/qml/Pages2/PageSetupWizardApiPremiumInfo.qml
Normal file
@@ -0,0 +1,297 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
// ApiServicesModel selection is amnezia-premium (set in PageSetupWizardApiServicesList before navigation).
|
||||
|
||||
property var subscriptionPlans: []
|
||||
property var benefitRows: []
|
||||
property int selectedGatewayPlanIndex: 0
|
||||
property string premiumFeaturesHtml: ""
|
||||
property string premiumHeaderName: ""
|
||||
property string premiumHeaderDescription: ""
|
||||
|
||||
readonly property var currentGatewayPlan: subscriptionPlans[selectedGatewayPlanIndex]
|
||||
|
||||
function syncFromModel() {
|
||||
root.subscriptionPlans = ApiServicesModel.getSelectedServiceData("subscriptionPlans")
|
||||
root.benefitRows = ApiServicesModel.getSelectedServiceData("premiumBenefitRows")
|
||||
|
||||
root.selectedGatewayPlanIndex = 0
|
||||
for (var i = 0; i < root.subscriptionPlans.length; ++i) {
|
||||
if (root.subscriptionPlans[i].recommended) {
|
||||
root.selectedGatewayPlanIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
root.premiumFeaturesHtml = String(ApiServicesModel.getSelectedServiceData("features")).replace("%1",
|
||||
LanguageModel.getCurrentSiteUrl("free")).replace("/free", "")
|
||||
root.premiumHeaderName = String(ApiServicesModel.getSelectedServiceData("name"))
|
||||
root.premiumHeaderDescription = String(ApiServicesModel.getSelectedServiceData("serviceDescription"))
|
||||
}
|
||||
|
||||
Component.onCompleted: syncFromModel()
|
||||
|
||||
Connections {
|
||||
target: ApiServicesModel
|
||||
|
||||
function onModelReset() {
|
||||
root.syncFromModel()
|
||||
}
|
||||
}
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
|
||||
onFocusChanged: {
|
||||
if (activeFocus) {
|
||||
flick.contentY = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: flick
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: continueButton.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
contentHeight: scrollColumn.implicitHeight + 24
|
||||
|
||||
ColumnLayout {
|
||||
id: scrollColumn
|
||||
|
||||
width: flick.width
|
||||
spacing: 0
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
headerText: root.premiumHeaderName
|
||||
descriptionText: root.premiumHeaderDescription
|
||||
}
|
||||
|
||||
LabelTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 12
|
||||
|
||||
text: qsTr("Choose a plan")
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
font.pixelSize: 13
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: subscriptionPlans.length
|
||||
|
||||
delegate: SubscriptionPlanCard {
|
||||
required property int index
|
||||
|
||||
readonly property var plan: root.subscriptionPlans[index]
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: index === root.subscriptionPlans.length - 1 ? 24 : 12
|
||||
|
||||
selected: root.selectedGatewayPlanIndex === index
|
||||
primaryLeft: String(plan.primary_left)
|
||||
primaryRight: String(plan.primary_right)
|
||||
subtitle: String(plan.subtitle)
|
||||
showRecommendedBadge: !!plan.recommended
|
||||
recommendedText: qsTr("Recommended")
|
||||
|
||||
onSelectRequested: root.selectedGatewayPlanIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
LabelTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 12
|
||||
|
||||
text: qsTr("Premium features")
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
font.pixelSize: 13
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
textFormat: Text.RichText
|
||||
text: root.premiumFeaturesHtml
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
|
||||
BenefitsPanel {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
benefitItems: root.benefitRows
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
visible: (Qt.platform.os === "ios" || IsMacOsNeBuild)
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
textFormat: Text.PlainText
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
font.pixelSize: 12
|
||||
|
||||
text: qsTr("Charged to your Apple ID at confirmation. Renews automatically unless auto-renew is turned off at least 24 hours before period end. Manage in Apple ID settings.")
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
visible: !(Qt.platform.os === "ios" || IsMacOsNeBuild)
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
textFormat: Text.RichText
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
font.pixelSize: 12
|
||||
|
||||
text: {
|
||||
var termsUrl = LanguageModel.getCurrentSiteUrl()
|
||||
var privacyUrl = LanguageModel.getCurrentSiteUrl("policy")
|
||||
return qsTr("By continuing, you agree to the <a href=\"%1\" style=\"color: #FBB26A;\">Terms of Use</a> and <a href=\"%2\" style=\"color: #FBB26A;\">Privacy Policy</a>").arg(termsUrl).arg(privacyUrl)
|
||||
}
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
visible: (Qt.platform.os === "ios" || IsMacOsNeBuild)
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
textFormat: Text.RichText
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
font.pixelSize: 12
|
||||
|
||||
text: {
|
||||
var termsUrl = "https://www.apple.com/legal/internet-services/itunes/dev/stdeula/"
|
||||
var privacyUrl = LanguageModel.getCurrentSiteUrl("policy")
|
||||
return qsTr("By continuing, you agree to the <a href=\"%1\" style=\"color: #FBB26A;\">Terms of Use</a> and <a href=\"%2\" style=\"color: #FBB26A;\">Privacy Policy</a>").arg(termsUrl).arg(privacyUrl)
|
||||
}
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: continueButton
|
||||
|
||||
z: 2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
anchors.bottomMargin: 16 + SettingsController.safeAreaBottomMargin
|
||||
|
||||
text: {
|
||||
var plan = root.currentGatewayPlan
|
||||
if (!plan) {
|
||||
return qsTr("Continue")
|
||||
}
|
||||
return qsTr("Subscribe — %1 for %2").arg(String(plan.primary_left)).arg(String(plan.primary_right))
|
||||
}
|
||||
|
||||
clickedFunc: function() {
|
||||
var plan = root.currentGatewayPlan
|
||||
if (!plan) {
|
||||
return
|
||||
}
|
||||
if (plan.checkout_url) {
|
||||
Qt.openUrlExternally(plan.checkout_url)
|
||||
PageController.closePage()
|
||||
PageController.closePage()
|
||||
return
|
||||
}
|
||||
if (plan.service_type) {
|
||||
var idx = ApiServicesModel.serviceIndexForType(plan.service_type)
|
||||
if (idx < 0) {
|
||||
return
|
||||
}
|
||||
ApiServicesModel.setServiceIndex(idx)
|
||||
PageController.showBusyIndicator(true)
|
||||
var ok = ApiConfigsController.importService()
|
||||
PageController.showBusyIndicator(false)
|
||||
if (!ok) {
|
||||
var endpoint = ApiServicesModel.getStoreEndpoint()
|
||||
Qt.openUrlExternally(endpoint)
|
||||
PageController.closePage()
|
||||
PageController.closePage()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,22 +97,6 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: (Qt.platform.os === "ios" || IsMacOsNeBuild) && ApiServicesModel.getSelectedServiceType() === "amnezia-premium"
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
textFormat: Text.PlainText
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
font.pixelSize: 12
|
||||
|
||||
text: qsTr("Charged to your Apple ID at confirmation. Renews automatically unless auto-renew is turned off at least 24 hours before period end. Manage in Apple ID settings.")
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: continueButton
|
||||
|
||||
@@ -122,7 +106,7 @@ PageType {
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: ApiServicesModel.getSelectedServiceType() === "amnezia-premium" ? qsTr("Subscribe Now") : (ApiServicesModel.getSelectedServiceType() === "amnezia-trial" ? qsTr("Try Trial") : qsTr("Connect"))
|
||||
text: ApiServicesModel.getSelectedServiceType() === "amnezia-trial" ? qsTr("Try Trial") : qsTr("Connect")
|
||||
|
||||
clickedFunc: function() {
|
||||
PageController.showBusyIndicator(true)
|
||||
@@ -138,36 +122,6 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 32
|
||||
|
||||
visible: (Qt.platform.os === "ios" || IsMacOsNeBuild) && ApiServicesModel.getSelectedServiceType() === "amnezia-premium"
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
textFormat: Text.RichText
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
font.pixelSize: 12
|
||||
|
||||
text: {
|
||||
var termsUrl = "https://www.apple.com/legal/internet-services/itunes/dev/stdeula/"
|
||||
var privacyUrl = LanguageModel.getCurrentSiteUrl("policy")
|
||||
return qsTr("By continuing, you agree to the <a href=\"%1\" style=\"color: #FBB26A;\">Terms of Use</a> and <a href=\"%2\" style=\"color: #FBB26A;\">Privacy Policy</a>").arg(termsUrl).arg(privacyUrl)
|
||||
}
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,11 @@ PageType {
|
||||
onClicked: {
|
||||
if (isServiceAvailable) {
|
||||
ApiServicesModel.setServiceIndex(proxyApiServicesModel.mapToSource(index))
|
||||
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
|
||||
if (ApiServicesModel.getSelectedServiceType() === "amnezia-premium") {
|
||||
PageController.goToPage(PageEnum.PageSetupWizardApiPremiumInfo)
|
||||
} else {
|
||||
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user