mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
feat: add subscription renewal (#2389)
* feat: add renewal subsribe * fix: after review
This commit is contained in:
@@ -96,6 +96,7 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
|
|||||||
const int httpStatusCodeConflict = 409;
|
const int httpStatusCodeConflict = 409;
|
||||||
const int httpStatusCodeNotFound = 404;
|
const int httpStatusCodeNotFound = 404;
|
||||||
const int httpStatusCodeNotImplemented = 501;
|
const int httpStatusCodeNotImplemented = 501;
|
||||||
|
const int httpStatusCodeUnprocessableEntity = 422;
|
||||||
|
|
||||||
if (!sslErrors.empty()) {
|
if (!sslErrors.empty()) {
|
||||||
qDebug().noquote() << sslErrors;
|
qDebug().noquote() << sslErrors;
|
||||||
@@ -128,6 +129,8 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
|
|||||||
return amnezia::ErrorCode::ApiNotFoundError;
|
return amnezia::ErrorCode::ApiNotFoundError;
|
||||||
} else if (httpStatusFromBody == httpStatusCodeNotImplemented) {
|
} else if (httpStatusFromBody == httpStatusCodeNotImplemented) {
|
||||||
return amnezia::ErrorCode::ApiUpdateRequestError;
|
return amnezia::ErrorCode::ApiUpdateRequestError;
|
||||||
|
} else if (httpStatusFromBody == httpStatusCodeUnprocessableEntity) {
|
||||||
|
return amnezia::ErrorCode::ApiSubscriptionExpiredError;
|
||||||
}
|
}
|
||||||
return amnezia::ErrorCode::ApiConfigDownloadError;
|
return amnezia::ErrorCode::ApiConfigDownloadError;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,6 +153,8 @@ void CoreController::initControllers()
|
|||||||
|
|
||||||
m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings));
|
m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings));
|
||||||
m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get());
|
m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get());
|
||||||
|
connect(m_apiConfigsController.get(), &ApiConfigsController::subscriptionExpiredOnServer,
|
||||||
|
m_apiAccountInfoModel.get(), &ApiAccountInfoModel::setSubscriptionExpiredByServer);
|
||||||
|
|
||||||
m_apiNewsController.reset(new ApiNewsController(m_newsModel, m_settings, m_serversModel, this));
|
m_apiNewsController.reset(new ApiNewsController(m_newsModel, m_settings, m_serversModel, this));
|
||||||
m_engine->rootContext()->setContextProperty("ApiNewsController", m_apiNewsController.get());
|
m_engine->rootContext()->setContextProperty("ApiNewsController", m_apiNewsController.get());
|
||||||
|
|||||||
@@ -135,6 +135,7 @@
|
|||||||
<file>ui/qml/Components/InstalledAppsDrawer.qml</file>
|
<file>ui/qml/Components/InstalledAppsDrawer.qml</file>
|
||||||
<file>ui/qml/Components/QuestionDrawer.qml</file>
|
<file>ui/qml/Components/QuestionDrawer.qml</file>
|
||||||
<file>ui/qml/Components/SelectLanguageDrawer.qml</file>
|
<file>ui/qml/Components/SelectLanguageDrawer.qml</file>
|
||||||
|
<file>ui/qml/Components/SubscriptionExpiredDrawer.qml</file>
|
||||||
<file>ui/qml/Components/ServersListView.qml</file>
|
<file>ui/qml/Components/ServersListView.qml</file>
|
||||||
<file>ui/qml/Components/SettingsContainersListView.qml</file>
|
<file>ui/qml/Components/SettingsContainersListView.qml</file>
|
||||||
<file>ui/qml/Components/TransportProtoSelector.qml</file>
|
<file>ui/qml/Components/TransportProtoSelector.qml</file>
|
||||||
|
|||||||
@@ -758,7 +758,11 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
emit errorOccurred(errorCode);
|
if (errorCode == ErrorCode::ApiSubscriptionExpiredError) {
|
||||||
|
emit subscriptionExpiredOnServer();
|
||||||
|
} else {
|
||||||
|
emit errorOccurred(errorCode);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public slots:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void errorOccurred(ErrorCode errorCode);
|
void errorOccurred(ErrorCode errorCode);
|
||||||
|
void subscriptionExpiredOnServer();
|
||||||
|
|
||||||
void installServerFromApiFinished(const QString &message);
|
void installServerFromApiFinished(const QString &message);
|
||||||
void changeApiCountryFinished(const QString &message);
|
void changeApiCountryFinished(const QString &message);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "apiSettingsController.h"
|
#include "apiSettingsController.h"
|
||||||
|
|
||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
|
#include <QJsonDocument>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include "core/api/apiUtils.h"
|
#include "core/api/apiUtils.h"
|
||||||
@@ -85,6 +86,42 @@ bool ApiSettingsController::getAccountInfo(bool reload)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApiSettingsController::getRenewalLink()
|
||||||
|
{
|
||||||
|
auto processedIndex = m_serversModel->getProcessedServerIndex();
|
||||||
|
auto serverConfig = m_serversModel->getServerConfig(processedIndex);
|
||||||
|
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
|
||||||
|
auto authData = serverConfig.value(configKey::authData).toObject();
|
||||||
|
|
||||||
|
bool isTestPurchase = apiConfig.value(apiDefs::key::isTestPurchase).toBool(false);
|
||||||
|
auto gatewayController = QSharedPointer<GatewayController>::create(m_settings->getGatewayEndpoint(isTestPurchase),
|
||||||
|
m_settings->isDevGatewayEnv(isTestPurchase),
|
||||||
|
requestTimeoutMsecs,
|
||||||
|
m_settings->isStrictKillSwitchEnabled());
|
||||||
|
|
||||||
|
QJsonObject apiPayload;
|
||||||
|
apiPayload[configKey::userCountryCode] = apiConfig.value(configKey::userCountryCode).toString();
|
||||||
|
apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString();
|
||||||
|
apiPayload[configKey::authData] = authData;
|
||||||
|
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||||
|
apiPayload[apiDefs::key::appLanguage] = m_settings->getAppLanguage().name().split("_").first();
|
||||||
|
|
||||||
|
auto future = gatewayController->postAsync(QString("%1v1/renewal_link"), apiPayload);
|
||||||
|
future.then(this, [this, gatewayController](QPair<ErrorCode, QByteArray> result) {
|
||||||
|
auto [errorCode, responseBody] = result;
|
||||||
|
if (errorCode != ErrorCode::NoError) {
|
||||||
|
emit errorOccurred(errorCode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject responseJson = QJsonDocument::fromJson(responseBody).object();
|
||||||
|
QString url = responseJson.value("url").toString();
|
||||||
|
if (!url.isEmpty()) {
|
||||||
|
emit renewalLinkReceived(url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void ApiSettingsController::updateApiCountryModel()
|
void ApiSettingsController::updateApiCountryModel()
|
||||||
{
|
{
|
||||||
m_apiCountryModel->updateModel(m_apiAccountInfoModel->getAvailableCountries(), "");
|
m_apiCountryModel->updateModel(m_apiAccountInfoModel->getAvailableCountries(), "");
|
||||||
|
|||||||
@@ -21,9 +21,11 @@ public slots:
|
|||||||
bool getAccountInfo(bool reload);
|
bool getAccountInfo(bool reload);
|
||||||
void updateApiCountryModel();
|
void updateApiCountryModel();
|
||||||
void updateApiDevicesModel();
|
void updateApiDevicesModel();
|
||||||
|
void getRenewalLink();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void errorOccurred(ErrorCode errorCode);
|
void errorOccurred(ErrorCode errorCode);
|
||||||
|
void renewalLinkReceived(const QString &url);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QSharedPointer<ServersModel> m_serversModel;
|
QSharedPointer<ServersModel> m_serversModel;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "apiAccountInfoModel.h"
|
#include "apiAccountInfoModel.h"
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
|
||||||
#include "core/api/apiUtils.h"
|
#include "core/api/apiUtils.h"
|
||||||
@@ -75,6 +76,19 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
case IsSubscriptionExpiredRole: {
|
||||||
|
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) return false;
|
||||||
|
if (m_isSubscriptionExpiredByServer) return true;
|
||||||
|
if (m_accountInfoData.subscriptionEndDate.isEmpty()) return false;
|
||||||
|
return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate);
|
||||||
|
}
|
||||||
|
case IsSubscriptionExpiringSoonRole: {
|
||||||
|
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) return false;
|
||||||
|
if (m_accountInfoData.subscriptionEndDate.isEmpty()) return false;
|
||||||
|
if (apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate)) return false;
|
||||||
|
QDateTime endDate = QDateTime::fromString(m_accountInfoData.subscriptionEndDate, Qt::ISODateWithMs);
|
||||||
|
return endDate <= QDateTime::currentDateTimeUtc().addDays(30);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
@@ -84,6 +98,8 @@ void ApiAccountInfoModel::updateModel(const QJsonObject &accountInfoObject, cons
|
|||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
|
|
||||||
|
m_isSubscriptionExpiredByServer = false;
|
||||||
|
|
||||||
AccountInfoData accountInfoData;
|
AccountInfoData accountInfoData;
|
||||||
|
|
||||||
m_availableCountries = accountInfoObject.value(apiDefs::key::availableCountries).toArray();
|
m_availableCountries = accountInfoObject.value(apiDefs::key::availableCountries).toArray();
|
||||||
@@ -108,6 +124,13 @@ void ApiAccountInfoModel::updateModel(const QJsonObject &accountInfoObject, cons
|
|||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApiAccountInfoModel::setSubscriptionExpiredByServer()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_isSubscriptionExpiredByServer = true;
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
QVariant ApiAccountInfoModel::data(const QString &roleString)
|
QVariant ApiAccountInfoModel::data(const QString &roleString)
|
||||||
{
|
{
|
||||||
QModelIndex modelIndex = index(0);
|
QModelIndex modelIndex = index(0);
|
||||||
@@ -166,6 +189,8 @@ QHash<int, QByteArray> ApiAccountInfoModel::roleNames() const
|
|||||||
roles[IsComponentVisibleRole] = "isComponentVisible";
|
roles[IsComponentVisibleRole] = "isComponentVisible";
|
||||||
roles[HasExpiredWorkerRole] = "hasExpiredWorker";
|
roles[HasExpiredWorkerRole] = "hasExpiredWorker";
|
||||||
roles[IsProtocolSelectionSupportedRole] = "isProtocolSelectionSupported";
|
roles[IsProtocolSelectionSupportedRole] = "isProtocolSelectionSupported";
|
||||||
|
roles[IsSubscriptionExpiredRole] = "isSubscriptionExpired";
|
||||||
|
roles[IsSubscriptionExpiringSoonRole] = "isSubscriptionExpiringSoon";
|
||||||
|
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ public:
|
|||||||
EndDateRole,
|
EndDateRole,
|
||||||
IsComponentVisibleRole,
|
IsComponentVisibleRole,
|
||||||
HasExpiredWorkerRole,
|
HasExpiredWorkerRole,
|
||||||
IsProtocolSelectionSupportedRole
|
IsProtocolSelectionSupportedRole,
|
||||||
|
IsSubscriptionExpiredRole,
|
||||||
|
IsSubscriptionExpiringSoonRole
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit ApiAccountInfoModel(QObject *parent = nullptr);
|
explicit ApiAccountInfoModel(QObject *parent = nullptr);
|
||||||
@@ -31,6 +33,7 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void updateModel(const QJsonObject &accountInfoObject, const QJsonObject &serverConfig);
|
void updateModel(const QJsonObject &accountInfoObject, const QJsonObject &serverConfig);
|
||||||
QVariant data(const QString &roleString);
|
QVariant data(const QString &roleString);
|
||||||
|
void setSubscriptionExpiredByServer();
|
||||||
|
|
||||||
QJsonArray getAvailableCountries();
|
QJsonArray getAvailableCountries();
|
||||||
QJsonArray getIssuedConfigsInfo();
|
QJsonArray getIssuedConfigsInfo();
|
||||||
@@ -59,6 +62,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
AccountInfoData m_accountInfoData;
|
AccountInfoData m_accountInfoData;
|
||||||
|
bool m_isSubscriptionExpiredByServer = false;
|
||||||
QJsonArray m_availableCountries;
|
QJsonArray m_availableCountries;
|
||||||
QJsonArray m_issuedConfigsInfo;
|
QJsonArray m_issuedConfigsInfo;
|
||||||
QJsonObject m_supportInfo;
|
QJsonObject m_supportInfo;
|
||||||
|
|||||||
113
client/ui/qml/Components/SubscriptionExpiredDrawer.qml
Normal file
113
client/ui/qml/Components/SubscriptionExpiredDrawer.qml
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
|
||||||
|
import PageEnum 1.0
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
|
DrawerType2 {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
expandedStateContent: ColumnLayout {
|
||||||
|
id: content
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
onImplicitHeightChanged: {
|
||||||
|
root.expandedHeight = content.implicitHeight + 32 + SettingsController.safeAreaBottomMargin
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 24
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
implicitHeight: titleText.implicitHeight
|
||||||
|
|
||||||
|
Header2TextType {
|
||||||
|
id: titleText
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: icon.left
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
|
||||||
|
text: qsTr("Amnezia Premium subscription has expired")
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: icon
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
width: 40
|
||||||
|
height: 40
|
||||||
|
source: "qrc:/images/controls/history.svg"
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorOverlay {
|
||||||
|
anchors.fill: icon
|
||||||
|
source: icon
|
||||||
|
color: AmneziaStyle.color.goldenApricot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ParagraphTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 8
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
|
text: qsTr("Renew your subscription to continue using VPN")
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicButtonType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
|
text: qsTr("Renew")
|
||||||
|
|
||||||
|
defaultColor: AmneziaStyle.color.paleGray
|
||||||
|
hoveredColor: AmneziaStyle.color.lightGray
|
||||||
|
pressedColor: AmneziaStyle.color.mutedGray
|
||||||
|
textColor: AmneziaStyle.color.midnightBlack
|
||||||
|
|
||||||
|
clickedFunc: function() {
|
||||||
|
ApiSettingsController.getRenewalLink()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicButtonType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 4
|
||||||
|
Layout.bottomMargin: 8
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
|
defaultColor: AmneziaStyle.color.transparent
|
||||||
|
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||||
|
pressedColor: AmneziaStyle.color.sheerWhite
|
||||||
|
textColor: AmneziaStyle.color.goldenApricot
|
||||||
|
|
||||||
|
text: qsTr("Support")
|
||||||
|
|
||||||
|
clickedFunc: function() {
|
||||||
|
root.closeTriggered()
|
||||||
|
PageController.goToPage(PageEnum.PageSettingsApiSupport)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,6 +52,26 @@ PageType {
|
|||||||
|
|
||||||
property var processedServer
|
property var processedServer
|
||||||
|
|
||||||
|
property bool isSubscriptionExpired: false
|
||||||
|
property bool isSubscriptionExpiringSoon: false
|
||||||
|
|
||||||
|
function updateSubscriptionState() {
|
||||||
|
root.isSubscriptionExpired = ApiAccountInfoModel.data("isSubscriptionExpired")
|
||||||
|
root.isSubscriptionExpiringSoon = ApiAccountInfoModel.data("isSubscriptionExpiringSoon")
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
root.updateSubscriptionState()
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: ApiAccountInfoModel
|
||||||
|
|
||||||
|
function onModelReset() {
|
||||||
|
root.updateSubscriptionState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ServersModel
|
target: ServersModel
|
||||||
|
|
||||||
@@ -114,6 +134,48 @@ PageType {
|
|||||||
serverNameEditDrawer.openTriggered()
|
serverNameEditDrawer.openTriggered()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
visible: root.isSubscriptionExpired || root.isSubscriptionExpiringSoon
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.topMargin: 4
|
||||||
|
|
||||||
|
text: root.isSubscriptionExpired
|
||||||
|
? qsTr("Subscription expired")
|
||||||
|
: qsTr("Subscription expiring soon")
|
||||||
|
|
||||||
|
color: root.isSubscriptionExpired
|
||||||
|
? AmneziaStyle.color.vibrantRed
|
||||||
|
: AmneziaStyle.color.goldenApricot
|
||||||
|
|
||||||
|
font.pixelSize: 14
|
||||||
|
font.weight: Font.Medium
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicButtonType {
|
||||||
|
visible: root.isSubscriptionExpired || root.isSubscriptionExpiringSoon
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.topMargin: 8
|
||||||
|
Layout.bottomMargin: 8
|
||||||
|
|
||||||
|
text: qsTr("Renew subscription")
|
||||||
|
|
||||||
|
defaultColor: AmneziaStyle.color.paleGray
|
||||||
|
hoveredColor: AmneziaStyle.color.lightGray
|
||||||
|
pressedColor: AmneziaStyle.color.mutedGray
|
||||||
|
textColor: AmneziaStyle.color.midnightBlack
|
||||||
|
|
||||||
|
clickedFunc: function() {
|
||||||
|
ApiSettingsController.getRenewalLink()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: ColumnLayout {
|
delegate: ColumnLayout {
|
||||||
|
|||||||
@@ -288,6 +288,34 @@ Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
objectName: "subscriptionExpiredDrawerItem"
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
SubscriptionExpiredDrawer {
|
||||||
|
id: subscriptionExpiredDrawer
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: ApiConfigsController
|
||||||
|
|
||||||
|
function onSubscriptionExpiredOnServer() {
|
||||||
|
subscriptionExpiredDrawer.openTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: ApiSettingsController
|
||||||
|
|
||||||
|
function onRenewalLinkReceived(url) {
|
||||||
|
Qt.openUrlExternally(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
objectName: "busyIndicatorItem"
|
objectName: "busyIndicatorItem"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user