mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-23 02:00:20 +07:00
feat: add trial api support
This commit is contained in:
@@ -234,6 +234,7 @@
|
|||||||
<file>ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml</file>
|
<file>ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSetupWizardApiFreeInfo.qml</file>
|
<file>ui/qml/Pages2/PageSetupWizardApiFreeInfo.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSetupWizardApiPremiumInfo.qml</file>
|
<file>ui/qml/Pages2/PageSetupWizardApiPremiumInfo.qml</file>
|
||||||
|
<file>ui/qml/Pages2/PageSetupWizardApiTrialEmail.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSetupWizardApiServicesList.qml</file>
|
<file>ui/qml/Pages2/PageSetupWizardApiServicesList.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSetupWizardConfigSource.qml</file>
|
<file>ui/qml/Pages2/PageSetupWizardConfigSource.qml</file>
|
||||||
<file>ui/qml/Pages2/PageSetupWizardCredentials.qml</file>
|
<file>ui/qml/Pages2/PageSetupWizardCredentials.qml</file>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
#include <QVariantMap>
|
||||||
|
|
||||||
#include "platforms/ios/ios_controller.h"
|
#include "platforms/ios/ios_controller.h"
|
||||||
|
|
||||||
@@ -459,7 +460,7 @@ bool ApiConfigsController::importService()
|
|||||||
return importSerivceFromAppStore();
|
return importSerivceFromAppStore();
|
||||||
}
|
}
|
||||||
} else if (m_apiServicesModel->getSelectedServiceType() == serviceType::amneziaFree) {
|
} else if (m_apiServicesModel->getSelectedServiceType() == serviceType::amneziaFree) {
|
||||||
importServiceFromGateway();
|
importFreeFromGateway();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -675,7 +676,7 @@ bool ApiConfigsController::restoreSerivceFromAppStore()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ApiConfigsController::importServiceFromGateway()
|
bool ApiConfigsController::importFreeFromGateway()
|
||||||
{
|
{
|
||||||
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||||
QString(APP_VERSION),
|
QString(APP_VERSION),
|
||||||
@@ -727,6 +728,72 @@ bool ApiConfigsController::importServiceFromGateway()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ApiConfigsController::importTrialFromGateway(const QString &email)
|
||||||
|
{
|
||||||
|
const QString trimmedEmail = email.trimmed();
|
||||||
|
if (trimmedEmail.isEmpty()) {
|
||||||
|
emit errorOccurred(ErrorCode::ApiConfigEmptyError);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
|
||||||
|
QString(APP_VERSION),
|
||||||
|
m_settings->getAppLanguage().name().split("_").first(),
|
||||||
|
m_settings->getInstallationUuid(true),
|
||||||
|
m_apiServicesModel->getCountryCode(),
|
||||||
|
"",
|
||||||
|
m_apiServicesModel->getSelectedServiceType(),
|
||||||
|
m_apiServicesModel->getSelectedServiceProtocol(),
|
||||||
|
QJsonObject() };
|
||||||
|
|
||||||
|
if (m_serversModel->isServerFromApiAlreadyExists(gatewayRequestData.userCountryCode, gatewayRequestData.serviceType,
|
||||||
|
gatewayRequestData.serviceProtocol)) {
|
||||||
|
emit errorOccurred(ErrorCode::ApiConfigAlreadyAdded);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolData protocolData = generateProtocolData(gatewayRequestData.serviceProtocol);
|
||||||
|
|
||||||
|
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
|
||||||
|
appendProtocolDataToApiPayload(gatewayRequestData.serviceProtocol, protocolData, apiPayload);
|
||||||
|
apiPayload.insert(apiDefs::key::email, trimmedEmail);
|
||||||
|
|
||||||
|
QByteArray responseBody;
|
||||||
|
ErrorCode errorCode = executeRequest(QString("%1v1/trial"), apiPayload, responseBody);
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
emit errorOccurred(errorCode);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject responseObject = QJsonDocument::fromJson(responseBody).object();
|
||||||
|
QString key = responseObject.value(apiDefs::key::config).toString();
|
||||||
|
if (key.isEmpty()) {
|
||||||
|
qWarning().noquote() << "[Trial] trial response does not contain config field";
|
||||||
|
emit errorOccurred(ErrorCode::ApiConfigEmptyError);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
key.replace(QStringLiteral("vpn://"), QString());
|
||||||
|
QByteArray configBytes = QByteArray::fromBase64(key.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
||||||
|
QByteArray uncompressed = qUncompress(configBytes);
|
||||||
|
if (!uncompressed.isEmpty()) {
|
||||||
|
configBytes = uncompressed;
|
||||||
|
}
|
||||||
|
if (configBytes.isEmpty()) {
|
||||||
|
qWarning().noquote() << "[Trial] trial response config payload is empty";
|
||||||
|
emit errorOccurred(ErrorCode::ApiConfigEmptyError);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject configObject = QJsonDocument::fromJson(configBytes).object();
|
||||||
|
quint16 crc = qChecksum(QJsonDocument(configObject).toJson());
|
||||||
|
configObject.insert(config_key::crc, crc);
|
||||||
|
m_serversModel->addServer(configObject);
|
||||||
|
|
||||||
|
emit installServerFromApiFinished(tr("%1 installed successfully.").arg(m_apiServicesModel->getSelectedServiceName()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
|
bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
|
||||||
bool reloadServiceConfig)
|
bool reloadServiceConfig)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,9 +18,6 @@ public:
|
|||||||
const QSharedPointer<ApiBenefitsModel> &benefitsModel, const std::shared_ptr<Settings> &settings,
|
const QSharedPointer<ApiBenefitsModel> &benefitsModel, const std::shared_ptr<Settings> &settings,
|
||||||
QObject *parent = nullptr);
|
QObject *parent = nullptr);
|
||||||
|
|
||||||
Q_PROPERTY(ApiSubscriptionPlansModel *subscriptionPlansModel READ subscriptionPlansModel CONSTANT)
|
|
||||||
Q_PROPERTY(ApiBenefitsModel *benefitsModel READ benefitsModel CONSTANT)
|
|
||||||
|
|
||||||
Q_PROPERTY(QList<QString> qrCodes READ getQrCodes NOTIFY vpnKeyExportReady)
|
Q_PROPERTY(QList<QString> qrCodes READ getQrCodes NOTIFY vpnKeyExportReady)
|
||||||
Q_PROPERTY(int qrCodesCount READ getQrCodesCount NOTIFY vpnKeyExportReady)
|
Q_PROPERTY(int qrCodesCount READ getQrCodesCount NOTIFY vpnKeyExportReady)
|
||||||
Q_PROPERTY(QString vpnKey READ getVpnKey NOTIFY vpnKeyExportReady)
|
Q_PROPERTY(QString vpnKey READ getVpnKey NOTIFY vpnKeyExportReady)
|
||||||
@@ -36,7 +33,8 @@ public slots:
|
|||||||
bool importService();
|
bool importService();
|
||||||
bool importSerivceFromAppStore();
|
bool importSerivceFromAppStore();
|
||||||
bool restoreSerivceFromAppStore();
|
bool restoreSerivceFromAppStore();
|
||||||
bool importServiceFromGateway();
|
bool importFreeFromGateway();
|
||||||
|
bool importTrialFromGateway(const QString &email);
|
||||||
bool updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
|
bool updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
|
||||||
bool reloadServiceConfig = false);
|
bool reloadServiceConfig = false);
|
||||||
bool updateServiceFromTelegram(const int serverIndex);
|
bool updateServiceFromTelegram(const int serverIndex);
|
||||||
@@ -45,9 +43,6 @@ public slots:
|
|||||||
|
|
||||||
bool isConfigValid();
|
bool isConfigValid();
|
||||||
|
|
||||||
ApiSubscriptionPlansModel *subscriptionPlansModel() const { return m_subscriptionPlansModel.get(); }
|
|
||||||
ApiBenefitsModel *benefitsModel() const { return m_benefitsModel.get(); }
|
|
||||||
|
|
||||||
void setCurrentProtocol(const QString &protocolName);
|
void setCurrentProtocol(const QString &protocolName);
|
||||||
bool isVlessProtocol();
|
bool isVlessProtocol();
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ namespace PageLoader
|
|||||||
PageShareConnection,
|
PageShareConnection,
|
||||||
|
|
||||||
PageSetupWizardApiPremiumInfo,
|
PageSetupWizardApiPremiumInfo,
|
||||||
|
PageSetupWizardApiTrialEmail,
|
||||||
|
|
||||||
PageDevMenu
|
PageDevMenu
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ namespace
|
|||||||
{
|
{
|
||||||
constexpr char amneziaFree[] = "amnezia-free";
|
constexpr char amneziaFree[] = "amnezia-free";
|
||||||
constexpr char amneziaPremium[] = "amnezia-premium";
|
constexpr char amneziaPremium[] = "amnezia-premium";
|
||||||
constexpr char amneziaTrial[] = "amnezia-trial";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +75,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
|
|||||||
}
|
}
|
||||||
case CardDescriptionRole: {
|
case CardDescriptionRole: {
|
||||||
auto speed = apiServiceData.serviceInfo.speed;
|
auto speed = apiServiceData.serviceInfo.speed;
|
||||||
if (serviceType == serviceType::amneziaPremium || serviceType == serviceType::amneziaTrial) {
|
if (serviceType == serviceType::amneziaPremium) {
|
||||||
return apiServiceData.serviceInfo.cardDescription.arg(speed);
|
return apiServiceData.serviceInfo.cardDescription.arg(speed);
|
||||||
} else if (serviceType == serviceType::amneziaFree) {
|
} else if (serviceType == serviceType::amneziaFree) {
|
||||||
QString description = apiServiceData.serviceInfo.cardDescription;
|
QString description = apiServiceData.serviceInfo.cardDescription;
|
||||||
@@ -132,11 +131,8 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
|
|||||||
if (serviceType == serviceType::amneziaPremium) {
|
if (serviceType == serviceType::amneziaPremium) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (serviceType == serviceType::amneziaTrial) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (serviceType == serviceType::amneziaFree) {
|
if (serviceType == serviceType::amneziaFree) {
|
||||||
return 2;
|
return 1;
|
||||||
}
|
}
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ namespace configKey
|
|||||||
constexpr char subtitle[] = "subtitle";
|
constexpr char subtitle[] = "subtitle";
|
||||||
constexpr char recommended[] = "recommended";
|
constexpr char recommended[] = "recommended";
|
||||||
constexpr char checkoutUrl[] = "checkout_url";
|
constexpr char checkoutUrl[] = "checkout_url";
|
||||||
constexpr char serviceType[] = "service_type";
|
constexpr char isTrial[] = "is_trial";
|
||||||
constexpr char serviceProtocol[] = "service_protocol";
|
constexpr char serviceProtocol[] = "service_protocol";
|
||||||
|
|
||||||
constexpr char primaryLeftCamel[] = "primaryLeft";
|
constexpr char primaryLeftCamel[] = "primaryLeft";
|
||||||
constexpr char primaryRightCamel[] = "primaryRight";
|
constexpr char primaryRightCamel[] = "primaryRight";
|
||||||
constexpr char checkoutUrlCamel[] = "checkoutUrl";
|
constexpr char checkoutUrlCamel[] = "checkoutUrl";
|
||||||
constexpr char serviceTypeCamel[] = "serviceType";
|
constexpr char isTrialCamel[] = "isTrial";
|
||||||
constexpr char serviceProtocolCamel[] = "serviceProtocol";
|
constexpr char serviceProtocolCamel[] = "serviceProtocol";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,8 +55,8 @@ QVariant ApiSubscriptionPlansModel::data(const QModelIndex &index, int role) con
|
|||||||
return plan.recommended;
|
return plan.recommended;
|
||||||
case CheckoutUrlRole:
|
case CheckoutUrlRole:
|
||||||
return plan.checkoutUrl;
|
return plan.checkoutUrl;
|
||||||
case ServiceTypeRole:
|
case IsTrialRole:
|
||||||
return plan.serviceType;
|
return plan.isTrial;
|
||||||
case ServiceProtocolRole:
|
case ServiceProtocolRole:
|
||||||
return plan.serviceProtocol;
|
return plan.serviceProtocol;
|
||||||
default:
|
default:
|
||||||
@@ -72,7 +72,7 @@ QHash<int, QByteArray> ApiSubscriptionPlansModel::roleNames() const
|
|||||||
{ SubtitleRole, "subtitle" },
|
{ SubtitleRole, "subtitle" },
|
||||||
{ RecommendedRole, "recommended" },
|
{ RecommendedRole, "recommended" },
|
||||||
{ CheckoutUrlRole, "checkoutUrl" },
|
{ CheckoutUrlRole, "checkoutUrl" },
|
||||||
{ ServiceTypeRole, "serviceType" },
|
{ IsTrialRole, "isTrial" },
|
||||||
{ ServiceProtocolRole, "serviceProtocol" },
|
{ ServiceProtocolRole, "serviceProtocol" },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,7 @@ void ApiSubscriptionPlansModel::updateModel(const QJsonArray &arr)
|
|||||||
subscriptionPlan.subtitle = planObject.value(configKey::subtitle).toString();
|
subscriptionPlan.subtitle = planObject.value(configKey::subtitle).toString();
|
||||||
subscriptionPlan.recommended = planObject.value(configKey::recommended).toBool();
|
subscriptionPlan.recommended = planObject.value(configKey::recommended).toBool();
|
||||||
subscriptionPlan.checkoutUrl = planObject.value(configKey::checkoutUrl).toString();
|
subscriptionPlan.checkoutUrl = planObject.value(configKey::checkoutUrl).toString();
|
||||||
subscriptionPlan.serviceType = planObject.value(configKey::serviceType).toString();
|
subscriptionPlan.isTrial = planObject.value(configKey::isTrial).toBool();
|
||||||
subscriptionPlan.serviceProtocol = planObject.value(configKey::serviceProtocol).toString();
|
subscriptionPlan.serviceProtocol = planObject.value(configKey::serviceProtocol).toString();
|
||||||
m_subscriptionPlans.append(std::move(subscriptionPlan));
|
m_subscriptionPlans.append(std::move(subscriptionPlan));
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ QVariantMap ApiSubscriptionPlansModel::planAt(int row) const
|
|||||||
planMap.insert(QLatin1String(configKey::subtitle), plan.subtitle);
|
planMap.insert(QLatin1String(configKey::subtitle), plan.subtitle);
|
||||||
planMap.insert(QLatin1String(configKey::recommended), plan.recommended);
|
planMap.insert(QLatin1String(configKey::recommended), plan.recommended);
|
||||||
planMap.insert(QLatin1String(configKey::checkoutUrlCamel), plan.checkoutUrl);
|
planMap.insert(QLatin1String(configKey::checkoutUrlCamel), plan.checkoutUrl);
|
||||||
planMap.insert(QLatin1String(configKey::serviceTypeCamel), plan.serviceType);
|
planMap.insert(QLatin1String(configKey::isTrialCamel), plan.isTrial);
|
||||||
planMap.insert(QLatin1String(configKey::serviceProtocolCamel), plan.serviceProtocol);
|
planMap.insert(QLatin1String(configKey::serviceProtocolCamel), plan.serviceProtocol);
|
||||||
return planMap;
|
return planMap;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public:
|
|||||||
SubtitleRole,
|
SubtitleRole,
|
||||||
RecommendedRole,
|
RecommendedRole,
|
||||||
CheckoutUrlRole,
|
CheckoutUrlRole,
|
||||||
ServiceTypeRole,
|
IsTrialRole,
|
||||||
ServiceProtocolRole
|
ServiceProtocolRole
|
||||||
};
|
};
|
||||||
Q_ENUM(Roles)
|
Q_ENUM(Roles)
|
||||||
@@ -41,7 +41,7 @@ private:
|
|||||||
QString subtitle;
|
QString subtitle;
|
||||||
bool recommended = false;
|
bool recommended = false;
|
||||||
QString checkoutUrl;
|
QString checkoutUrl;
|
||||||
QString serviceType;
|
bool isTrial = false;
|
||||||
QString serviceProtocol;
|
QString serviceProtocol;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ PageType {
|
|||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.bottomMargin: 12
|
Layout.bottomMargin: 12
|
||||||
|
|
||||||
text: qsTr("Available with Free")
|
text: qsTr("Free features")
|
||||||
color: AmneziaStyle.color.mutedGray
|
color: AmneziaStyle.color.mutedGray
|
||||||
font.pixelSize: 13
|
font.pixelSize: 13
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,7 @@ PageType {
|
|||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.bottomMargin: 24
|
Layout.bottomMargin: 24
|
||||||
|
|
||||||
benefitsModel: ApiConfigsController.benefitsModel
|
benefitsModel: ApiBenefitsModel
|
||||||
}
|
}
|
||||||
|
|
||||||
ParagraphTextType {
|
ParagraphTextType {
|
||||||
@@ -94,7 +94,7 @@ PageType {
|
|||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.bottomMargin: 16
|
Layout.bottomMargin: 16
|
||||||
|
|
||||||
visible: root.freeFeaturesHtml.length > 0 && ApiConfigsController.benefitsModel.rowCount() === 0
|
visible: root.freeFeaturesHtml.length > 0 && ApiBenefitsModel.rowCount() === 0
|
||||||
|
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
text: root.freeFeaturesHtml
|
text: root.freeFeaturesHtml
|
||||||
@@ -127,7 +127,7 @@ PageType {
|
|||||||
var termsUrl = LanguageModel.getCurrentSiteUrl()
|
var termsUrl = LanguageModel.getCurrentSiteUrl()
|
||||||
var privacyUrl = LanguageModel.getCurrentSiteUrl("policy")
|
var privacyUrl = LanguageModel.getCurrentSiteUrl("policy")
|
||||||
return qsTr("By continuing, you agree to the <a href=\"%1\" style=\"color: %3;\">Terms of Use</a> and <a href=\"%2\" style=\"color: %3;\">Privacy Policy</a>")
|
return qsTr("By continuing, you agree to the <a href=\"%1\" style=\"color: %3;\">Terms of Use</a> and <a href=\"%2\" style=\"color: %3;\">Privacy Policy</a>")
|
||||||
.arg(termsUrl).arg(privacyUrl).arg(Qt.colorToString(AmneziaStyle.color.goldenApricot))
|
.arg(termsUrl).arg(privacyUrl).arg("#FBB26A")
|
||||||
}
|
}
|
||||||
|
|
||||||
onLinkActivated: function(link) {
|
onLinkActivated: function(link) {
|
||||||
@@ -158,7 +158,7 @@ PageType {
|
|||||||
var termsUrl = "https://www.apple.com/legal/internet-services/itunes/dev/stdeula/"
|
var termsUrl = "https://www.apple.com/legal/internet-services/itunes/dev/stdeula/"
|
||||||
var privacyUrl = LanguageModel.getCurrentSiteUrl("policy")
|
var privacyUrl = LanguageModel.getCurrentSiteUrl("policy")
|
||||||
return qsTr("By continuing, you agree to the <a href=\"%1\" style=\"color: %3;\">Terms of Use</a> and <a href=\"%2\" style=\"color: %3;\">Privacy Policy</a>")
|
return qsTr("By continuing, you agree to the <a href=\"%1\" style=\"color: %3;\">Terms of Use</a> and <a href=\"%2\" style=\"color: %3;\">Privacy Policy</a>")
|
||||||
.arg(termsUrl).arg(privacyUrl).arg(Qt.colorToString(AmneziaStyle.color.goldenApricot))
|
.arg(termsUrl).arg(privacyUrl).arg("#FBB26A")
|
||||||
}
|
}
|
||||||
|
|
||||||
onLinkActivated: function(link) {
|
onLinkActivated: function(link) {
|
||||||
@@ -185,7 +185,7 @@ PageType {
|
|||||||
anchors.rightMargin: 16
|
anchors.rightMargin: 16
|
||||||
anchors.bottomMargin: 16 + SettingsController.safeAreaBottomMargin
|
anchors.bottomMargin: 16 + SettingsController.safeAreaBottomMargin
|
||||||
|
|
||||||
text: ApiServicesModel.getSelectedServiceType() === "amnezia-trial" ? qsTr("Try Trial") : qsTr("Continue")
|
text: qsTr("Continue")
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
PageController.showBusyIndicator(true)
|
PageController.showBusyIndicator(true)
|
||||||
|
|||||||
@@ -9,21 +9,20 @@ import "../Controls2"
|
|||||||
import "../Controls2/TextTypes"
|
import "../Controls2/TextTypes"
|
||||||
import "../Config"
|
import "../Config"
|
||||||
import "../Components"
|
import "../Components"
|
||||||
|
import PageEnum 1.0
|
||||||
|
|
||||||
PageType {
|
PageType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property int selectedPlanIndex: 0
|
property int selectedPlanIndex: 0
|
||||||
property string premiumFeaturesHtml: ""
|
|
||||||
property string premiumHeaderName: ""
|
property string premiumHeaderName: ""
|
||||||
property string premiumHeaderDescription: ""
|
property string premiumHeaderDescription: ""
|
||||||
|
|
||||||
readonly property var currentPlan: ApiConfigsController.subscriptionPlansModel.planAt(selectedPlanIndex)
|
readonly property var currentPlan: ApiSubscriptionPlansModel.planAt(selectedPlanIndex)
|
||||||
|
|
||||||
function syncFromModel() {
|
function syncFromModel() {
|
||||||
root.selectedPlanIndex = ApiConfigsController.subscriptionPlansModel.recommendedRowIndex()
|
root.selectedPlanIndex = ApiSubscriptionPlansModel.recommendedRowIndex()
|
||||||
|
|
||||||
root.premiumFeaturesHtml = String(ApiServicesModel.getSelectedServiceData("features")).replace("%1", LanguageModel.getCurrentSiteUrl("free")).replace("/free", "")
|
|
||||||
root.premiumHeaderName = String(ApiServicesModel.getSelectedServiceData("name"))
|
root.premiumHeaderName = String(ApiServicesModel.getSelectedServiceData("name"))
|
||||||
root.premiumHeaderDescription = String(ApiServicesModel.getSelectedServiceData("serviceDescription"))
|
root.premiumHeaderDescription = String(ApiServicesModel.getSelectedServiceData("serviceDescription"))
|
||||||
}
|
}
|
||||||
@@ -72,27 +71,17 @@ PageType {
|
|||||||
descriptionText: root.premiumHeaderDescription
|
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 {
|
Repeater {
|
||||||
model: ApiConfigsController.subscriptionPlansModel
|
model: ApiSubscriptionPlansModel
|
||||||
|
|
||||||
delegate: SubscriptionPlanCard {
|
delegate: SubscriptionPlanCard {
|
||||||
required property int index
|
required property int index
|
||||||
|
required property var model
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.bottomMargin: index === ApiConfigsController.subscriptionPlansModel.rowCount() - 1 ? 24 : 12
|
Layout.bottomMargin: index === ApiSubscriptionPlansModel.rowCount() - 1 ? 24 : 12
|
||||||
|
|
||||||
selected: root.selectedPlanIndex === index
|
selected: root.selectedPlanIndex === index
|
||||||
primaryLeft: String(model.primaryLeft)
|
primaryLeft: String(model.primaryLeft)
|
||||||
@@ -116,32 +105,13 @@ PageType {
|
|||||||
font.pixelSize: 13
|
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 {
|
BenefitsPanel {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.bottomMargin: 24
|
Layout.bottomMargin: 24
|
||||||
|
|
||||||
benefitsModel: ApiConfigsController.benefitsModel
|
benefitsModel: ApiBenefitsModel
|
||||||
}
|
}
|
||||||
|
|
||||||
ParagraphTextType {
|
ParagraphTextType {
|
||||||
@@ -177,7 +147,7 @@ PageType {
|
|||||||
var termsUrl = LanguageModel.getCurrentSiteUrl()
|
var termsUrl = LanguageModel.getCurrentSiteUrl()
|
||||||
var privacyUrl = LanguageModel.getCurrentSiteUrl("policy")
|
var privacyUrl = LanguageModel.getCurrentSiteUrl("policy")
|
||||||
return qsTr("By continuing, you agree to the <a href=\"%1\" style=\"color: %3;\">Terms of Use</a> and <a href=\"%2\" style=\"color: %3;\">Privacy Policy</a>")
|
return qsTr("By continuing, you agree to the <a href=\"%1\" style=\"color: %3;\">Terms of Use</a> and <a href=\"%2\" style=\"color: %3;\">Privacy Policy</a>")
|
||||||
.arg(termsUrl).arg(privacyUrl).arg(Qt.colorToString(AmneziaStyle.color.goldenApricot))
|
.arg(termsUrl).arg(privacyUrl).arg("#FBB26A")
|
||||||
}
|
}
|
||||||
|
|
||||||
onLinkActivated: function(link) {
|
onLinkActivated: function(link) {
|
||||||
@@ -208,7 +178,7 @@ PageType {
|
|||||||
var termsUrl = "https://www.apple.com/legal/internet-services/itunes/dev/stdeula/"
|
var termsUrl = "https://www.apple.com/legal/internet-services/itunes/dev/stdeula/"
|
||||||
var privacyUrl = LanguageModel.getCurrentSiteUrl("policy")
|
var privacyUrl = LanguageModel.getCurrentSiteUrl("policy")
|
||||||
return qsTr("By continuing, you agree to the <a href=\"%1\" style=\"color: %3;\">Terms of Use</a> and <a href=\"%2\" style=\"color: %3;\">Privacy Policy</a>")
|
return qsTr("By continuing, you agree to the <a href=\"%1\" style=\"color: %3;\">Terms of Use</a> and <a href=\"%2\" style=\"color: %3;\">Privacy Policy</a>")
|
||||||
.arg(termsUrl).arg(privacyUrl).arg(Qt.colorToString(AmneziaStyle.color.goldenApricot))
|
.arg(termsUrl).arg(privacyUrl).arg("#FBB26A")
|
||||||
}
|
}
|
||||||
|
|
||||||
onLinkActivated: function(link) {
|
onLinkActivated: function(link) {
|
||||||
@@ -248,27 +218,24 @@ PageType {
|
|||||||
if (!plan) {
|
if (!plan) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (plan.isTrial) {
|
||||||
|
PageController.goToPage(PageEnum.PageSetupWizardApiTrialEmail)
|
||||||
|
return
|
||||||
|
}
|
||||||
if (plan.checkoutUrl) {
|
if (plan.checkoutUrl) {
|
||||||
Qt.openUrlExternally(plan.checkoutUrl)
|
Qt.openUrlExternally(plan.checkoutUrl)
|
||||||
PageController.closePage()
|
PageController.closePage()
|
||||||
PageController.closePage()
|
PageController.closePage()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (plan.serviceType) {
|
PageController.showBusyIndicator(true)
|
||||||
var idx = ApiServicesModel.serviceIndexForType(plan.serviceType)
|
var ok = ApiConfigsController.importService()
|
||||||
if (idx < 0) {
|
PageController.showBusyIndicator(false)
|
||||||
return
|
if (!ok) {
|
||||||
}
|
var endpoint = ApiServicesModel.getStoreEndpoint()
|
||||||
ApiServicesModel.setServiceIndex(idx)
|
Qt.openUrlExternally(endpoint)
|
||||||
PageController.showBusyIndicator(true)
|
PageController.closePage()
|
||||||
var ok = ApiConfigsController.importService()
|
PageController.closePage()
|
||||||
PageController.showBusyIndicator(false)
|
|
||||||
if (!ok) {
|
|
||||||
var endpoint = ApiServicesModel.getStoreEndpoint()
|
|
||||||
Qt.openUrlExternally(endpoint)
|
|
||||||
PageController.closePage()
|
|
||||||
PageController.closePage()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import PageEnum 1.0
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "./"
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
import "../Config"
|
||||||
|
import "../Components"
|
||||||
|
|
||||||
|
PageType {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
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: qsTr("Create an account")
|
||||||
|
descriptionText: qsTr("To manage your subscription")
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFieldWithHeaderType {
|
||||||
|
id: emailField
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.bottomMargin: 24
|
||||||
|
|
||||||
|
headerText: qsTr("Email")
|
||||||
|
textField.placeholderText: qsTr("Email")
|
||||||
|
textField.inputMethodHints: Qt.ImhEmailCharactersOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
ParagraphTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.bottomMargin: 24
|
||||||
|
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
color: AmneziaStyle.color.mutedGray
|
||||||
|
font.pixelSize: 12
|
||||||
|
text: qsTr("Additional details for this step can be described here.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: qsTr("Continue")
|
||||||
|
|
||||||
|
clickedFunc: function() {
|
||||||
|
var raw = emailField.textField.text.trim()
|
||||||
|
if (raw.length === 0 || raw.indexOf("@") < 0) {
|
||||||
|
PageController.showNotificationMessage(qsTr("Enter a valid email address"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
PageController.showBusyIndicator(true)
|
||||||
|
var ok = ApiConfigsController.importTrialFromGateway(raw)
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
|
if (ok) {
|
||||||
|
PageController.closePage()
|
||||||
|
PageController.closePage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user