feat: add base amnezia trial support (#2366)

* feat: add base amnezia trial support

* feat: add external-trial
This commit is contained in:
vkamn
2026-03-24 09:29:51 +07:00
committed by GitHub
parent 40e39895c9
commit c57162c4cc
6 changed files with 249 additions and 233 deletions

View File

@@ -10,8 +10,10 @@ namespace apiDefs
AmneziaFreeV3, AmneziaFreeV3,
AmneziaPremiumV1, AmneziaPremiumV1,
AmneziaPremiumV2, AmneziaPremiumV2,
AmneziaTrialV2,
SelfHosted, SelfHosted,
ExternalPremium ExternalPremium,
ExternalTrial
}; };
enum ConfigSource { enum ConfigSource {

View File

@@ -58,18 +58,24 @@ apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObjec
}; };
case apiDefs::ConfigSource::AmneziaGateway: { case apiDefs::ConfigSource::AmneziaGateway: {
constexpr QLatin1String servicePremium("amnezia-premium"); constexpr QLatin1String servicePremium("amnezia-premium");
constexpr QLatin1String serviceTrial("amnezia-trial");
constexpr QLatin1String serviceFree("amnezia-free"); constexpr QLatin1String serviceFree("amnezia-free");
constexpr QLatin1String serviceExternalPremium("external-premium"); constexpr QLatin1String serviceExternalPremium("external-premium");
constexpr QLatin1String serviceExternalTrial("external-trial");
auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject(); auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject();
auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString(); auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString();
if (serviceType == servicePremium) { if (serviceType == servicePremium) {
return apiDefs::ConfigType::AmneziaPremiumV2; return apiDefs::ConfigType::AmneziaPremiumV2;
} else if (serviceType == serviceTrial) {
return apiDefs::ConfigType::AmneziaTrialV2;
} else if (serviceType == serviceFree) { } else if (serviceType == serviceFree) {
return apiDefs::ConfigType::AmneziaFreeV3; return apiDefs::ConfigType::AmneziaFreeV3;
} else if (serviceType == serviceExternalPremium) { } else if (serviceType == serviceExternalPremium) {
return apiDefs::ConfigType::ExternalPremium; return apiDefs::ConfigType::ExternalPremium;
} else if (serviceType == serviceExternalTrial) {
return apiDefs::ConfigType::ExternalTrial;
} }
} }
default: { default: {
@@ -133,7 +139,8 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject) bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject)
{ {
static const QSet<apiDefs::ConfigType> premiumTypes = { apiDefs::ConfigType::AmneziaPremiumV1, apiDefs::ConfigType::AmneziaPremiumV2, static const QSet<apiDefs::ConfigType> premiumTypes = { apiDefs::ConfigType::AmneziaPremiumV1, apiDefs::ConfigType::AmneziaPremiumV2,
apiDefs::ConfigType::ExternalPremium }; apiDefs::ConfigType::AmneziaTrialV2, apiDefs::ConfigType::ExternalPremium,
apiDefs::ConfigType::ExternalTrial };
return premiumTypes.contains(getConfigType(serverConfigObject)); return premiumTypes.contains(getConfigType(serverConfigObject));
} }
@@ -177,7 +184,9 @@ QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject) QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject)
{ {
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV2) { auto configType = apiUtils::getConfigType(serverConfigObject);
if (configType != apiDefs::ConfigType::AmneziaPremiumV2 && configType != apiDefs::ConfigType::AmneziaTrialV2
&& configType != apiDefs::ConfigType::ExternalPremium && configType != apiDefs::ConfigType::ExternalTrial) {
return {}; return {};
} }

View File

@@ -447,7 +447,7 @@ bool ApiConfigsController::importService()
importSerivceFromAppStore(); importSerivceFromAppStore();
return true; return true;
} }
} else { } else if (m_apiServicesModel->getSelectedServiceType() == serviceType::amneziaFree) {
importServiceFromGateway(); importServiceFromGateway();
return true; return true;
} }

View File

@@ -52,7 +52,9 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
} }
case IsComponentVisibleRole: { case IsComponentVisibleRole: {
return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2 return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2
|| m_accountInfoData.configType == apiDefs::ConfigType::ExternalPremium; || m_accountInfoData.configType == apiDefs::ConfigType::AmneziaTrialV2
|| m_accountInfoData.configType == apiDefs::ConfigType::ExternalPremium
|| m_accountInfoData.configType == apiDefs::ConfigType::ExternalTrial;
} }
case HasExpiredWorkerRole: { case HasExpiredWorkerRole: {
for (int i = 0; i < m_issuedConfigsInfo.size(); i++) { for (int i = 0; i < m_issuedConfigsInfo.size(); i++) {

View File

@@ -41,6 +41,7 @@ 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";
} }
} }
@@ -69,7 +70,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) { if (serviceType == serviceType::amneziaPremium || serviceType == serviceType::amneziaTrial) {
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;
@@ -124,8 +125,10 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
case OrderRole: { case OrderRole: {
if (serviceType == serviceType::amneziaPremium) { if (serviceType == serviceType::amneziaPremium) {
return 0; return 0;
} else if (serviceType == serviceType::amneziaFree) { } else if (serviceType == serviceType::amneziaTrial) {
return 1; return 1;
} else if (serviceType == serviceType::amneziaFree) {
return 2;
} }
} }
} }

View File

@@ -1,226 +1,226 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs import QtQuick.Dialogs
import PageEnum 1.0 import PageEnum 1.0
import Style 1.0 import Style 1.0
import "./" import "./"
import "../Controls2" import "../Controls2"
import "../Controls2/TextTypes" import "../Controls2/TextTypes"
import "../Config" import "../Config"
import "../Components" import "../Components"
PageType { PageType {
id: root id: root
BackButtonType { BackButtonType {
id: backButton id: backButton
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
onFocusChanged: { onFocusChanged: {
if (this.activeFocus) { if (this.activeFocus) {
listView.positionViewAtBeginning() listView.positionViewAtBeginning()
} }
} }
} }
ListViewType { ListViewType {
id: listView id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
header: ColumnLayout { header: ColumnLayout {
width: listView.width width: listView.width
BaseHeaderType { BaseHeaderType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8 Layout.topMargin: 8
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.bottomMargin: 32 Layout.bottomMargin: 32
headerText: ApiServicesModel.getSelectedServiceData("name") headerText: ApiServicesModel.getSelectedServiceData("name")
descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription") descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription")
} }
} }
model: inputFields model: inputFields
spacing: 0 spacing: 0
delegate: ColumnLayout { delegate: ColumnLayout {
width: listView.width width: listView.width
LabelWithImageType { LabelWithImageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16 Layout.margins: 16
imageSource: imagePath imageSource: imagePath
leftText: lText leftText: lText
rightText: rText rightText: rText
visible: isVisible visible: isVisible
} }
} }
footer: ColumnLayout { footer: ColumnLayout {
width: listView.width width: listView.width
spacing: 0 spacing: 0
ParagraphTextType { ParagraphTextType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
onLinkActivated: function(link) { onLinkActivated: function(link) {
Qt.openUrlExternally(link) Qt.openUrlExternally(link)
} }
textFormat: Text.RichText textFormat: Text.RichText
text: { text: {
var text = ApiServicesModel.getSelectedServiceData("features") var text = ApiServicesModel.getSelectedServiceData("features")
return text.replace("%1", LanguageModel.getCurrentSiteUrl("free")).replace("/free", "") // todo link should come from gateway return text.replace("%1", LanguageModel.getCurrentSiteUrl("free")).replace("/free", "") // todo link should come from gateway
} }
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.NoButton acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
} }
} }
ParagraphTextType { ParagraphTextType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
visible: (Qt.platform.os === "ios" || IsMacOsNeBuild) && ApiServicesModel.getSelectedServiceType() === "amnezia-premium" visible: (Qt.platform.os === "ios" || IsMacOsNeBuild) && ApiServicesModel.getSelectedServiceType() === "amnezia-premium"
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
textFormat: Text.PlainText textFormat: Text.PlainText
color: AmneziaStyle.color.mutedGray color: AmneziaStyle.color.mutedGray
font.pixelSize: 12 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.") 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 { BasicButtonType {
id: continueButton id: continueButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.bottomMargin: 16 Layout.bottomMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
text: ApiServicesModel.getSelectedServiceType() === "amnezia-premium" ? qsTr("Subscribe Now") : qsTr("Connect") text: ApiServicesModel.getSelectedServiceType() === "amnezia-premium" ? qsTr("Subscribe Now") : (ApiServicesModel.getSelectedServiceType() === "amnezia-trial" ? qsTr("Try Trial") : qsTr("Connect"))
clickedFunc: function() { clickedFunc: function() {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
var result = ApiConfigsController.importService() var result = ApiConfigsController.importService()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
if (!result) { if (!result) {
var endpoint = ApiServicesModel.getStoreEndpoint() var endpoint = ApiServicesModel.getStoreEndpoint()
Qt.openUrlExternally(endpoint) Qt.openUrlExternally(endpoint)
PageController.closePage() PageController.closePage()
PageController.closePage() PageController.closePage()
} }
} }
} }
ParagraphTextType { ParagraphTextType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.bottomMargin: 32 Layout.bottomMargin: 32
visible: (Qt.platform.os === "ios" || IsMacOsNeBuild) && ApiServicesModel.getSelectedServiceType() === "amnezia-premium" visible: (Qt.platform.os === "ios" || IsMacOsNeBuild) && ApiServicesModel.getSelectedServiceType() === "amnezia-premium"
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
textFormat: Text.RichText textFormat: Text.RichText
color: AmneziaStyle.color.mutedGray color: AmneziaStyle.color.mutedGray
font.pixelSize: 12 font.pixelSize: 12
text: { text: {
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: #FBB26A;\">Terms of Use</a> and <a href=\"%2\" style=\"color: #FBB26A;\">Privacy Policy</a>").arg(termsUrl).arg(privacyUrl) 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) { onLinkActivated: function(link) {
Qt.openUrlExternally(link) Qt.openUrlExternally(link)
} }
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.NoButton acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
} }
} }
} }
} }
property list<QtObject> inputFields: [ property list<QtObject> inputFields: [
region, region,
price, price,
timeLimit, timeLimit,
speed, speed,
features features
] ]
QtObject { QtObject {
id: region id: region
readonly property string imagePath: "qrc:/images/controls/map-pin.svg" readonly property string imagePath: "qrc:/images/controls/map-pin.svg"
readonly property string lText: qsTr("For the region") readonly property string lText: qsTr("For the region")
readonly property string rText: ApiServicesModel.getSelectedServiceData("region") readonly property string rText: ApiServicesModel.getSelectedServiceData("region")
property bool isVisible: true property bool isVisible: true
} }
QtObject { QtObject {
id: price id: price
readonly property string imagePath: "qrc:/images/controls/tag.svg" readonly property string imagePath: "qrc:/images/controls/tag.svg"
readonly property string lText: qsTr("Price") readonly property string lText: qsTr("Price")
readonly property string rText: ApiServicesModel.getSelectedServiceData("price") readonly property string rText: ApiServicesModel.getSelectedServiceData("price")
property bool isVisible: true property bool isVisible: true
} }
QtObject { QtObject {
id: timeLimit id: timeLimit
readonly property string imagePath: "qrc:/images/controls/history.svg" readonly property string imagePath: "qrc:/images/controls/history.svg"
readonly property string lText: qsTr("Work period") readonly property string lText: qsTr("Work period")
readonly property string rText: ApiServicesModel.getSelectedServiceData("timeLimit") readonly property string rText: ApiServicesModel.getSelectedServiceData("timeLimit")
property bool isVisible: rText !== "" property bool isVisible: rText !== ""
} }
QtObject { QtObject {
id: speed id: speed
readonly property string imagePath: "qrc:/images/controls/gauge.svg" readonly property string imagePath: "qrc:/images/controls/gauge.svg"
readonly property string lText: qsTr("Speed") readonly property string lText: qsTr("Speed")
readonly property string rText: ApiServicesModel.getSelectedServiceData("speed") readonly property string rText: ApiServicesModel.getSelectedServiceData("speed")
property bool isVisible: true property bool isVisible: true
} }
QtObject { QtObject {
id: features id: features
readonly property string imagePath: "qrc:/images/controls/info.svg" readonly property string imagePath: "qrc:/images/controls/info.svg"
readonly property string lText: qsTr("Features") readonly property string lText: qsTr("Features")
readonly property string rText: "" readonly property string rText: ""
property bool isVisible: true property bool isVisible: true
} }
} }