fix: ui fixes for renewal subscription (#2406)

This commit is contained in:
NickVs2015
2026-03-25 07:34:42 +03:00
committed by GitHub
parent f0f0f7c5be
commit 9a0222aee3
8 changed files with 193 additions and 37 deletions

View File

@@ -337,26 +337,26 @@ class AmneziaActivity : QtActivity() {
private external fun nativeGamepadKeyEvent(deviceId: Int, keyCode: Int, pressed: Boolean) private external fun nativeGamepadKeyEvent(deviceId: Int, keyCode: Int, pressed: Boolean)
override fun onPause() { override fun onPause() {
// Notify Qt to stop rendering BEFORE super.onPause() destroys the EGL surface.
// Using a coroutine here would be too late — the surface is gone by the time
// the coroutine runs. A direct synchronous call gives Qt's render thread the
// best chance to process visible=false before surface destruction.
if (qtInitialized.isCompleted) {
QtAndroidController.onActivityPaused()
}
super.onPause() super.onPause()
isActivityResumed = false isActivityResumed = false
// Cancel all pending operations when activity pauses // Cancel all pending operations when activity pauses
resumeHandler.removeCallbacksAndMessages(null) resumeHandler.removeCallbacksAndMessages(null)
openFileDeliveryScheduled = false openFileDeliveryScheduled = false
Log.d(TAG, "Pause Amnezia activity") Log.d(TAG, "Pause Amnezia activity")
// Notify Qt to stop rendering before the EGL surface is disconnected
mainScope.launch {
qtInitialized.await()
QtAndroidController.onActivityPaused()
}
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
isActivityResumed = true isActivityResumed = true
Log.d(TAG, "Resume Amnezia activity") Log.d(TAG, "Resume Amnezia activity")
// Notify Qt to resume rendering after surface reconnects if (qtInitialized.isCompleted) {
mainScope.launch {
qtInitialized.await()
QtAndroidController.onActivityResumed() QtAndroidController.onActivityResumed()
} }

View File

@@ -46,6 +46,7 @@ namespace
constexpr int httpStatusCodeConflict = 409; constexpr int httpStatusCodeConflict = 409;
constexpr int httpStatusCodeNotImplemented = 501; constexpr int httpStatusCodeNotImplemented = 501;
constexpr int httpStatusCodeUnprocessableEntity = 422;
} }
GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs, GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
@@ -451,6 +452,8 @@ bool GatewayController::shouldBypassProxy(const QNetworkReply::NetworkError &rep
} }
} else if (httpStatus == httpStatusCodeConflict) { } else if (httpStatus == httpStatusCodeConflict) {
return false; return false;
} else if (httpStatus == httpStatusCodeUnprocessableEntity) {
return false;
} else if (replyError != QNetworkReply::NetworkError::NoError) { } else if (replyError != QNetworkReply::NetworkError::NoError) {
qDebug() << replyError; qDebug() << replyError;
return true; return true;

View File

@@ -33,7 +33,7 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
} }
return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate) ? tr("<p><a style=\"color: #EB5757;\">Inactive</a>") return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate) ? tr("<p><a style=\"color: #EB5757;\">Inactive</a>")
: tr("Active"); : tr("<p><a style=\"color: #28c840;\">Active</a>");
} }
case EndDateRole: { case EndDateRole: {
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) { if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {

View File

@@ -19,6 +19,15 @@ ListViewType {
id: root id: root
property int selectedIndex: ServersModel.defaultIndex property int selectedIndex: ServersModel.defaultIndex
property int expiredServerIndex: -1
property bool expiringSoon: false
Connections {
target: ApiAccountInfoModel
function onModelReset() {
root.expiringSoon = ApiAccountInfoModel.data("isSubscriptionExpiringSoon")
}
}
anchors.top: serversMenuHeader.bottom anchors.top: serversMenuHeader.bottom
anchors.right: parent.right anchors.right: parent.right
@@ -35,6 +44,13 @@ ListViewType {
} }
} }
Connections {
target: ApiConfigsController
function onSubscriptionExpiredOnServer() {
root.expiredServerIndex = ServersModel.defaultIndex
}
}
delegate: Item { delegate: Item {
id: menuContentDelegate id: menuContentDelegate
objectName: "menuContentDelegate" objectName: "menuContentDelegate"
@@ -126,6 +142,18 @@ ListViewType {
} }
} }
CaptionTextType {
visible: isServerFromGatewayApi && (index === root.expiredServerIndex || (root.expiringSoon && index === root.selectedIndex && index !== root.expiredServerIndex))
Layout.fillWidth: true
Layout.leftMargin: 64
Layout.bottomMargin: 8
text: index === root.expiredServerIndex ? qsTr("Subscription expired. Please renew.") : qsTr("Subscription expiring soon.")
color: index === root.expiredServerIndex ? AmneziaStyle.color.vibrantRed : AmneziaStyle.color.goldenApricot
wrapMode: Text.WordWrap
}
DividerType { DividerType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 0 Layout.leftMargin: 0

View File

@@ -37,29 +37,11 @@ DrawerType2 {
Header2TextType { Header2TextType {
id: titleText id: titleText
anchors.left: parent.left anchors.left: parent.left
anchors.right: icon.left anchors.right: parent.right
anchors.rightMargin: 8
text: qsTr("Amnezia Premium subscription has expired") text: qsTr("Amnezia Premium subscription has expired")
horizontalAlignment: Text.AlignLeft 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 { ParagraphTextType {
@@ -91,11 +73,11 @@ DrawerType2 {
} }
BasicButtonType { BasicButtonType {
Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 4 Layout.topMargin: 8
Layout.bottomMargin: 8 Layout.bottomMargin: 8
Layout.rightMargin: 16
Layout.leftMargin: 16 implicitHeight: 25
defaultColor: AmneziaStyle.color.transparent defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite hoveredColor: AmneziaStyle.color.translucentWhite

View File

@@ -1,6 +1,7 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Style 1.0 import Style 1.0
@@ -37,6 +38,7 @@ Item {
property int borderFocusedWidth: 1 property int borderFocusedWidth: 1
property string rightImageColor: AmneziaStyle.color.paleGray property string rightImageColor: AmneziaStyle.color.paleGray
property string leftImageColor: ""
property bool descriptionOnTop: false property bool descriptionOnTop: false
property bool hideDescription: true property bool hideDescription: true
@@ -140,6 +142,14 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
source: leftImageSource source: leftImageSource
visible: leftImageColor === ""
}
ColorOverlay {
anchors.fill: leftImage
source: leftImage
color: leftImageColor
visible: leftImageColor !== ""
} }
} }

View File

@@ -18,6 +18,22 @@ PageType {
id: root id: root
property var processedServer property var processedServer
property bool subscriptionExpired: false
property bool subscriptionExpiringSoon: false
function updateSubscriptionState() {
root.subscriptionExpiringSoon = ApiAccountInfoModel.data("isSubscriptionExpiringSoon")
}
Component.onCompleted: {
root.updateSubscriptionState()
}
Connections {
target: ApiAccountInfoModel
function onModelReset() {
root.updateSubscriptionState()
}
}
Connections { Connections {
target: ServersModel target: ServersModel
@@ -27,6 +43,15 @@ PageType {
} }
} }
Connections {
target: ApiConfigsController
function onSubscriptionExpiredOnServer() {
root.subscriptionExpired = true
root.subscriptionExpiringSoon = false
}
}
SortFilterProxyModel { SortFilterProxyModel {
id: proxyServersModel id: proxyServersModel
objectName: "proxyServersModel" objectName: "proxyServersModel"
@@ -76,12 +101,11 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.bottomMargin: 10 Layout.bottomMargin: 4
actionButtonImage: "qrc:/images/controls/settings.svg" actionButtonImage: "qrc:/images/controls/settings.svg"
headerText: root.processedServer.name headerText: root.processedServer.name
descriptionText: qsTr("Location for connection")
actionButtonFunction: function() { actionButtonFunction: function() {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
@@ -94,6 +118,50 @@ PageType {
PageController.goToPage(PageEnum.PageSettingsApiServerInfo) PageController.goToPage(PageEnum.PageSettingsApiServerInfo)
} }
} }
CaptionTextType {
visible: root.subscriptionExpired || root.subscriptionExpiringSoon
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 4
text: root.subscriptionExpired ? qsTr("Subscription expired") : qsTr("Subscription expiring soon")
color: root.subscriptionExpired ? AmneziaStyle.color.vibrantRed : AmneziaStyle.color.goldenApricot
}
BasicButtonType {
visible: root.subscriptionExpired || root.subscriptionExpiringSoon
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 8
Layout.bottomMargin: 4
defaultColor: AmneziaStyle.color.paleGray
hoveredColor: AmneziaStyle.color.lightGray
pressedColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.midnightBlack
text: qsTr("Renew subscription")
clickedFunc: function() {
ApiSettingsController.getRenewalLink()
}
}
CaptionTextType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: (root.subscriptionExpired || root.subscriptionExpiringSoon) ? 8 : 4
Layout.bottomMargin: 8
text: qsTr("Location for connection")
color: AmneziaStyle.color.mutedGray
}
} }
delegate: ColumnLayout { delegate: ColumnLayout {

View File

@@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs import QtQuick.Dialogs
import Qt5Compat.GraphicalEffects
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
@@ -128,7 +129,6 @@ PageType {
actionButtonImage: "qrc:/images/controls/edit-3.svg" actionButtonImage: "qrc:/images/controls/edit-3.svg"
headerText: root.processedServer.name headerText: root.processedServer.name
descriptionText: ApiAccountInfoModel.data("serviceDescription")
actionButtonFunction: function() { actionButtonFunction: function() {
serverNameEditDrawer.openTriggered() serverNameEditDrawer.openTriggered()
@@ -156,6 +156,19 @@ PageType {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
} }
ParagraphTextType {
visible: ApiAccountInfoModel.data("serviceDescription") !== ""
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 16
Layout.bottomMargin: root.isSubscriptionExpired || root.isSubscriptionExpiringSoon ? 0 : 10
text: ApiAccountInfoModel.data("serviceDescription")
color: AmneziaStyle.color.mutedGray
}
BasicButtonType { BasicButtonType {
visible: root.isSubscriptionExpired || root.isSubscriptionExpiringSoon visible: root.isSubscriptionExpired || root.isSubscriptionExpiringSoon
@@ -213,6 +226,54 @@ PageType {
readonly property bool isVisibleForAmneziaFree: ApiAccountInfoModel.data("isComponentVisible") readonly property bool isVisibleForAmneziaFree: ApiAccountInfoModel.data("isComponentVisible")
Item {
visible: !root.isSubscriptionExpired && !root.isSubscriptionExpiringSoon
Layout.fillWidth: true
implicitHeight: renewRow.implicitHeight + 32
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: ApiSettingsController.getRenewalLink()
}
Row {
id: renewRow
anchors.centerIn: parent
spacing: 12
Item {
width: renewIcon.implicitWidth
height: renewIcon.implicitHeight
anchors.verticalCenter: parent.verticalCenter
Image {
id: renewIcon
source: "qrc:/images/controls/refresh-cw.svg"
}
ColorOverlay {
anchors.fill: renewIcon
source: renewIcon
color: AmneziaStyle.color.goldenApricot
}
}
Text {
text: qsTr("Renew subscription")
color: AmneziaStyle.color.goldenApricot
font.pixelSize: 18
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
}
DividerType {
visible: !root.isSubscriptionExpired && !root.isSubscriptionExpiringSoon
}
SwitcherType { SwitcherType {
id: switcher id: switcher
@@ -239,10 +300,14 @@ PageType {
} }
} }
DividerType {
visible: footer.isVisibleForAmneziaFree
}
WarningType { WarningType {
id: warning id: warning
Layout.topMargin: 32 Layout.topMargin: 24
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.fillWidth: true Layout.fillWidth: true
@@ -266,7 +331,7 @@ PageType {
id: vpnKey id: vpnKey
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: warning.visible ? 16 : 32 Layout.topMargin: warning.visible ? 16 : 0
visible: footer.isVisibleForAmneziaFree visible: footer.isVisibleForAmneziaFree