feature: added error handling and minor ui fixes

This commit is contained in:
vladimir.kuznetsov 2025-02-10 15:10:59 +07:00
parent 42d3d9b98a
commit 07baf0ed65
16 changed files with 101 additions and 53 deletions

View file

@ -213,21 +213,6 @@ void ApiController::updateServerConfigFromApi(const QString &installationUuid, c
} }
} }
ErrorCode ApiController::getAccountInfo(const QString &userCountryCode, const QString &serviceType, const QJsonObject &authData,
QByteArray &responseBody)
{
GatewayController gatewayController(m_gatewayEndpoint, m_isDevEnvironment, requestTimeoutMsecs);
QJsonObject apiPayload;
apiPayload[configKey::userCountryCode] = userCountryCode;
apiPayload[configKey::serviceType] = serviceType;
apiPayload[configKey::authData] = authData;
ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody);
return errorCode;
}
ErrorCode ApiController::getServicesList(QByteArray &responseBody) ErrorCode ApiController::getServicesList(QByteArray &responseBody)
{ {
GatewayController gatewayController(m_gatewayEndpoint, m_isDevEnvironment, requestTimeoutMsecs); GatewayController gatewayController(m_gatewayEndpoint, m_isDevEnvironment, requestTimeoutMsecs);

View file

@ -18,9 +18,6 @@ public:
public slots: public slots:
void updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig); void updateServerConfigFromApi(const QString &installationUuid, const int serverIndex, QJsonObject serverConfig);
ErrorCode getAccountInfo(const QString &userCountryCode, const QString &serviceType, const QJsonObject &authData,
QByteArray &responseBody);
ErrorCode getServicesList(QByteArray &responseBody); ErrorCode getServicesList(QByteArray &responseBody);
ErrorCode getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType, ErrorCode getConfigForService(const QString &installationUuid, const QString &userCountryCode, const QString &serviceType,
const QString &protocol, const QString &serverCountryCode, const QJsonObject &authData, const QString &protocol, const QString &serverCountryCode, const QJsonObject &authData,

View file

@ -48,8 +48,13 @@ ApiConfigsController::ApiConfigsController(const QSharedPointer<ServersModel> &s
{ {
} }
void ApiConfigsController::exportNativeConfig(const QString &serverCountryCode, const QString &fileName) bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode, const QString &fileName)
{ {
if (fileName.isEmpty()) {
emit errorOccurred(ErrorCode::PermissionsError);
return false;
}
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs); GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex()); auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex());
@ -67,14 +72,16 @@ void ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
QByteArray responseBody; QByteArray responseBody;
ErrorCode errorCode = gatewayController.post(QString("%1v1/native_config"), apiPayload, responseBody); ErrorCode errorCode = gatewayController.post(QString("%1v1/native_config"), apiPayload, responseBody);
// // if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
// // } return false;
}
QJsonObject jsonConfig = QJsonDocument::fromJson(responseBody).object(); QJsonObject jsonConfig = QJsonDocument::fromJson(responseBody).object();
QString nativeConfig = jsonConfig.value(configKey::config).toString(); QString nativeConfig = jsonConfig.value(configKey::config).toString();
SystemController::saveFile(fileName, nativeConfig); SystemController::saveFile(fileName, nativeConfig);
return true;
} }
ApiConfigsController::ApiPayloadData ApiConfigsController::generateApiPayloadData(const QString &protocol) ApiConfigsController::ApiPayloadData ApiConfigsController::generateApiPayloadData(const QString &protocol)

View file

@ -14,7 +14,10 @@ public:
QObject *parent = nullptr); QObject *parent = nullptr);
public slots: public slots:
void exportNativeConfig(const QString &serverCountryCode, const QString &fileName); bool exportNativeConfig(const QString &serverCountryCode, const QString &fileName);
signals:
void errorOccurred(ErrorCode errorCode);
private: private:
struct ApiPayloadData struct ApiPayloadData

View file

@ -1,5 +1,6 @@
#include "apiSettingsController.h" #include "apiSettingsController.h"
#include "core/api/apiUtils.h"
#include "core/controllers/gatewayController.h" #include "core/controllers/gatewayController.h"
namespace namespace
@ -49,11 +50,14 @@ bool ApiSettingsController::getAccountInfo()
apiPayload[configKey::authData] = authData; apiPayload[configKey::authData] = authData;
QByteArray responseBody; QByteArray responseBody;
if (apiUtils::getConfigType(serverConfig) == apiDefs::ConfigType::AmneziaPremiumV2) {
ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody); ErrorCode errorCode = gatewayController.post(QString("%1v1/account_info"), apiPayload, responseBody);
if (errorCode != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
// emit errorOccured(errorCode); emit errorOccurred(errorCode);
return false; return false;
} }
}
QJsonObject accountInfo = QJsonDocument::fromJson(responseBody).object(); QJsonObject accountInfo = QJsonDocument::fromJson(responseBody).object();
m_apiAccountInfoModel->updateModel(accountInfo, serverConfig); m_apiAccountInfoModel->updateModel(accountInfo, serverConfig);

View file

@ -20,6 +20,9 @@ public slots:
bool getAccountInfo(); bool getAccountInfo();
void updateApiCountryModel(); void updateApiCountryModel();
signals:
void errorOccurred(ErrorCode errorCode);
private: private:
QSharedPointer<ServersModel> m_serversModel; QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel; QSharedPointer<ApiAccountInfoModel> m_apiAccountInfoModel;

View file

@ -12,9 +12,6 @@ namespace
namespace configKey namespace configKey
{ {
constexpr char availableCountries[] = "available_countries"; constexpr char availableCountries[] = "available_countries";
// constexpr char serverCountryCode[] = "server_country_code";
// constexpr char serverCountryName[] = "server_country_name";
// constexpr char lastUpdated[] = "last_updated";
constexpr char activeDeviceCount[] = "active_device_count"; constexpr char activeDeviceCount[] = "active_device_count";
constexpr char maxDeviceCount[] = "max_device_count"; constexpr char maxDeviceCount[] = "max_device_count";
constexpr char subscriptionEndDate[] = "subscription_end_date"; constexpr char subscriptionEndDate[] = "subscription_end_date";
@ -38,12 +35,23 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
switch (role) { switch (role) {
case SubscriptionStatusRole: { case SubscriptionStatusRole: {
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {
return tr("Active");
}
return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate) ? tr("Inactive") : tr("Active"); return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate) ? tr("Inactive") : tr("Active");
} }
case EndDateRole: { case EndDateRole: {
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {
return "";
}
return QDateTime::fromString(m_accountInfoData.subscriptionEndDate, Qt::ISODate).toLocalTime().toString("d MMM yyyy"); return QDateTime::fromString(m_accountInfoData.subscriptionEndDate, Qt::ISODate).toLocalTime().toString("d MMM yyyy");
} }
case ConnectedDevicesRole: { case ConnectedDevicesRole: {
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {
return "";
}
return tr("%1 out of %2").arg(m_accountInfoData.activeDeviceCount).arg(m_accountInfoData.maxDeviceCount); return tr("%1 out of %2").arg(m_accountInfoData.activeDeviceCount).arg(m_accountInfoData.maxDeviceCount);
} }
case ServiceDescriptionRole: { case ServiceDescriptionRole: {
@ -55,6 +63,9 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
"more. YouTube is not included in the free plan."); "more. YouTube is not included in the free plan.");
} }
} }
case IsComponentVisibleRole: {
return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2;
}
} }
return QVariant(); return QVariant();
@ -67,15 +78,6 @@ void ApiAccountInfoModel::updateModel(const QJsonObject &accountInfoObject, cons
AccountInfoData accountInfoData; AccountInfoData accountInfoData;
m_availableCountries = accountInfoObject.value(configKey::availableCountries).toArray(); m_availableCountries = accountInfoObject.value(configKey::availableCountries).toArray();
// for (const auto &country : availableCountries) {
// auto countryObject = country.toObject();
// CountryInfo countryInfo;
// countryInfo.serverCountryCode = countryObject.value(configKey::serverCountryCode).toString();
// countryInfo.serverCountryName = countryObject.value(configKey::serverCountryName).toString();
// countryInfo.lastUpdated = countryObject.value(configKey::lastUpdated).toString();
// accountInfoData.AvailableCountries.push_back(countryInfo);
// }
accountInfoData.activeDeviceCount = accountInfoObject.value(configKey::activeDeviceCount).toInt(); accountInfoData.activeDeviceCount = accountInfoObject.value(configKey::activeDeviceCount).toInt();
accountInfoData.maxDeviceCount = accountInfoObject.value(configKey::maxDeviceCount).toInt(); accountInfoData.maxDeviceCount = accountInfoObject.value(configKey::maxDeviceCount).toInt();
@ -106,6 +108,16 @@ QJsonArray ApiAccountInfoModel::getAvailableCountries()
return m_availableCountries; return m_availableCountries;
} }
QString ApiAccountInfoModel::getTelegramBotLink()
{
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {
return tr("amnezia_free_support_bot");
} else if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2) {
return tr("amnezia_premium_support_bot");
}
return "";
}
QHash<int, QByteArray> ApiAccountInfoModel::roleNames() const QHash<int, QByteArray> ApiAccountInfoModel::roleNames() const
{ {
QHash<int, QByteArray> roles; QHash<int, QByteArray> roles;
@ -113,6 +125,7 @@ QHash<int, QByteArray> ApiAccountInfoModel::roleNames() const
roles[EndDateRole] = "endDate"; roles[EndDateRole] = "endDate";
roles[ConnectedDevicesRole] = "connectedDevices"; roles[ConnectedDevicesRole] = "connectedDevices";
roles[ServiceDescriptionRole] = "serviceDescription"; roles[ServiceDescriptionRole] = "serviceDescription";
roles[IsComponentVisibleRole] = "isComponentVisible";
return roles; return roles;
} }

View file

@ -16,7 +16,8 @@ public:
SubscriptionStatusRole = Qt::UserRole + 1, SubscriptionStatusRole = Qt::UserRole + 1,
ConnectedDevicesRole, ConnectedDevicesRole,
ServiceDescriptionRole, ServiceDescriptionRole,
EndDateRole EndDateRole,
IsComponentVisibleRole
}; };
explicit ApiAccountInfoModel(QObject *parent = nullptr); explicit ApiAccountInfoModel(QObject *parent = nullptr);
@ -30,6 +31,7 @@ public slots:
QVariant data(const QString &roleString); QVariant data(const QString &roleString);
QJsonArray getAvailableCountries(); QJsonArray getAvailableCountries();
QString getTelegramBotLink();
protected: protected:
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;

View file

@ -116,8 +116,11 @@ ListView {
PageController.goToPage(PageEnum.PageSettingsApiAvailableCountries) PageController.goToPage(PageEnum.PageSettingsApiAvailableCountries)
} else { } else {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
ApiSettingsController.getAccountInfo() let result = ApiSettingsController.getAccountInfo()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
if (!result) {
return
}
PageController.goToPage(PageEnum.PageSettingsApiServerInfo) PageController.goToPage(PageEnum.PageSettingsApiServerInfo)
} }

View file

@ -303,8 +303,11 @@ PageType {
PageController.goToPage(PageEnum.PageSettingsApiAvailableCountries) PageController.goToPage(PageEnum.PageSettingsApiAvailableCountries)
} else { } else {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
ApiSettingsController.getAccountInfo() let result = ApiSettingsController.getAccountInfo()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
if (!result) {
return
}
PageController.goToPage(PageEnum.PageSettingsApiServerInfo) PageController.goToPage(PageEnum.PageSettingsApiServerInfo)
} }

View file

@ -89,12 +89,15 @@ PageType {
actionButtonImage: "qrc:/images/controls/settings.svg" actionButtonImage: "qrc:/images/controls/settings.svg"
headerText: root.processedServer.name headerText: root.processedServer.name
descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription") descriptionText: qsTr("Locations for connection")
actionButtonFunction: function() { actionButtonFunction: function() {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
ApiSettingsController.getAccountInfo() let result = ApiSettingsController.getAccountInfo()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
if (!result) {
return
}
PageController.goToPage(PageEnum.PageSettingsApiServerInfo) PageController.goToPage(PageEnum.PageSettingsApiServerInfo)
} }

View file

@ -75,8 +75,12 @@ PageType {
} }
if (fileName !== "") { if (fileName !== "") {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
ApiConfigsController.exportNativeConfig(countryCode, fileName) let result = ApiConfigsController.exportNativeConfig(countryCode, fileName)
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
if (result) {
PageController.showNotificationMessage(qsTr("Config file saved"))
}
} }
} }
} }

View file

@ -141,16 +141,20 @@ PageType {
} }
footer: ColumnLayout { footer: ColumnLayout {
id: footer
width: listView.width width: listView.width
spacing: 0 spacing: 0
readonly property bool isVisibleForAmneziaFree: ApiAccountInfoModel.data("isComponentVisible")
LabelWithButtonType { LabelWithButtonType {
id: vpnKey id: vpnKey
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
visible: false visible: footer.isVisibleForAmneziaFree
text: qsTr("Subscription key") text: qsTr("Subscription key")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
@ -160,12 +164,13 @@ PageType {
} }
DividerType { DividerType {
visible: false visible: footer.isVisibleForAmneziaFree
} }
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: vpnKey.visible ? 0 : 32
visible: footer.isVisibleForAmneziaFree
text: qsTr("Configuration files") text: qsTr("Configuration files")
@ -178,10 +183,13 @@ PageType {
} }
} }
DividerType {} DividerType {
visible: footer.isVisibleForAmneziaFree
}
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: footer.isVisibleForAmneziaFree ? 0 : 32
text: qsTr("Support") text: qsTr("Support")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"

View file

@ -44,12 +44,14 @@ PageType {
LabelWithButtonType { LabelWithButtonType {
Layout.fillWidth: true Layout.fillWidth: true
readonly property string telegramBotLink: ApiAccountInfoModel.getTelegramBotLink()
text: qsTr("Telegram") text: qsTr("Telegram")
descriptionText: qsTr("@amnezia_premium_support_bot") descriptionText: "@" + telegramBotLink
rightImageSource: "qrc:/images/controls/external-link.svg" rightImageSource: "qrc:/images/controls/external-link.svg"
clickedFunction: function() { clickedFunction: function() {
Qt.openUrlExternally(qsTr("https://t.me/amnezia_premium_support_bot")) Qt.openUrlExternally("https://t.me/" + telegramBotLink)
} }
} }

View file

@ -96,8 +96,11 @@ PageType {
if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) { if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
ApiSettingsController.getAccountInfo() let result = ApiSettingsController.getAccountInfo()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
if (!result) {
return
}
PageController.goToPage(PageEnum.PageSettingsApiServerInfo) PageController.goToPage(PageEnum.PageSettingsApiServerInfo)
} else { } else {

View file

@ -224,6 +224,14 @@ PageType {
} }
} }
Connections {
target: ApiSettingsController
function onErrorOccurred(error) {
PageController.showErrorMessage(error)
}
}
StackViewType { StackViewType {
id: tabBarStackView id: tabBarStackView
objectName: "tabBarStackView" objectName: "tabBarStackView"