mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 12:08:41 +00:00
plan upgrade does work
This commit is contained in:
parent
a795aea308
commit
e7129614fa
15 changed files with 783 additions and 178 deletions
|
|
@ -169,13 +169,12 @@
|
|||
"errorPlanLimitReached": "Du hast das Limit deines Plans erreicht. Bitte upgrade deinen Plan.",
|
||||
"errorPlanNotAllowed": "Dieses Feature ist in deinem aktuellen Plan nicht verfügbar.",
|
||||
"errorVoucherInvalid": "Der eingegebene Gutschein-Code ist nicht gültig.",
|
||||
"proYearlyPrice": "10€/Jahr",
|
||||
"proMonthlyPrice": "1€/Monat",
|
||||
"errorPlanUpgradeNotYearly": "Das Upgrade des Plans muss jährlich bezahlt werden, da der aktuelle Plan ebenfalls jährlich abgerechnet wird.",
|
||||
"proFeature1": "✓ Unbegrenzte Medien-Datei-Uploads",
|
||||
"proFeature2": "1 zusätzlicher Plus Benutzer",
|
||||
"proFeature3": "3 zusätzliche kostenlose Benutzer",
|
||||
"familyYearlyPrice": "20€/Jahr",
|
||||
"familyMonthlyPrice": "2€/Monat",
|
||||
"year": "year",
|
||||
"month": "month",
|
||||
"familyFeature1": "✓ Unbegrenzte Medien-Datei-Uploads",
|
||||
"familyFeature2": "4 zusätzliche Plus Benutzer",
|
||||
"familyFeature3": "5 zusätzliche kostenlose Benutzer",
|
||||
|
|
@ -183,7 +182,7 @@
|
|||
"freeFeature1": "3 Medien-Datei-Uploads pro Tag",
|
||||
"plusFeature1": "✓ Unbegrenzte Medien-Datei-Uploads",
|
||||
"transactionHistory": "Transaktionshistorie",
|
||||
"currentBalance": "Aktueller Kontostand",
|
||||
"currentBalance": "Dein Guthaben",
|
||||
"manageAdditionalUsers": "Zusätzlichen Benutzer verwalten",
|
||||
"open": "Offene",
|
||||
"buy": "Kaufen",
|
||||
|
|
@ -203,5 +202,16 @@
|
|||
"transactionThanksForTesting": "Danke fürs Testen",
|
||||
"transactionUnknown": "Unbekannte Transaktion",
|
||||
"transactionVoucherCreated": "Gutschein erstellt",
|
||||
"transactionVoucherRedeemed": "Gutschein eingelöst"
|
||||
"transactionVoucherRedeemed": "Gutschein eingelöst",
|
||||
"checkoutOptions": "Optionen",
|
||||
"checkoutPayYearly": "Jährlich bezahlen",
|
||||
"checkoutTotal": "Gesamt",
|
||||
"selectPaymentMethode": "Zahlungsmethode auswählen",
|
||||
"twonlyCredit": "twonly-Guthaben",
|
||||
"notEnoughCredit": "Du hast nicht genügend Guthaben!",
|
||||
"chargeCredit": "Guthaben aufladen",
|
||||
"autoRenewal": "Automatische Verlängerung",
|
||||
"autoRenewalDesc": "Du kannst dies jederzeit ändern.",
|
||||
"planSuccessUpgraded": "Dein Plan wurde erfolgreich aktualisiert.",
|
||||
"checkoutSubmit": "Kostenpflichtig bestellen"
|
||||
}
|
||||
|
|
@ -326,14 +326,13 @@
|
|||
"errorVoucherInvalid": "The voucher code you entered is not valid.",
|
||||
"errorPlanLimitReached": "You have reached your plans limit. Please upgrade your plan.",
|
||||
"errorPlanNotAllowed": "This feature is not available in your current plan.",
|
||||
"errorPlanUpgradeNotYearly": "The plan upgrade must be paid for annually, as the current plan is also billed annually.",
|
||||
"upgradeToPaidPlan": "Upgrade to a paid plan.",
|
||||
"proYearlyPrice": "10€/year",
|
||||
"proMonthlyPrice": "1€/month",
|
||||
"year": "year",
|
||||
"month": "month",
|
||||
"proFeature1": "✓ Unlimited media file uploads",
|
||||
"proFeature2": "1 additional Plus user",
|
||||
"proFeature3": "3 additional Free users",
|
||||
"familyYearlyPrice": "20€/year",
|
||||
"familyMonthlyPrice": "2€/month",
|
||||
"familyFeature1": "✓ Unlimited media file uploads",
|
||||
"familyFeature2": "4 additional Plus users",
|
||||
"familyFeature3": "5 additional Free users",
|
||||
|
|
@ -341,6 +340,8 @@
|
|||
"freeFeature1": "3 Media file uploads per day",
|
||||
"plusFeature1": "✓ Unlimited media file uploads",
|
||||
"transactionHistory": "Your transaction history",
|
||||
"manageSubscription": "Manage your subscription",
|
||||
"nextPayment": "Next payment",
|
||||
"currentBalance": "Current balance",
|
||||
"manageAdditionalUsers": "Manage your additional users",
|
||||
"open": "Open",
|
||||
|
|
@ -361,5 +362,17 @@
|
|||
"transactionThanksForTesting": "Thank you for testing",
|
||||
"transactionUnknown": "Unknown transaction",
|
||||
"transactionVoucherCreated": "Voucher created",
|
||||
"transactionVoucherRedeemed": "Voucher redeemed"
|
||||
"transactionVoucherRedeemed": "Voucher redeemed",
|
||||
"checkoutOptions": "Options",
|
||||
"checkoutPayYearly": "Pay yearly",
|
||||
"checkoutTotal": "Total",
|
||||
"selectPaymentMethode": "Select Payment Method",
|
||||
"twonlyCredit": "twonly-Credit",
|
||||
"notEnoughCredit": "You do not have enough credit!",
|
||||
"chargeCredit": "Charge credit",
|
||||
"chargeCredit": "Charge credit",
|
||||
"autoRenewal": "Auto renewal",
|
||||
"autoRenewalDesc": "You can change this at any time.",
|
||||
"planSuccessUpgraded": "Successfully upgraded your plan.",
|
||||
"checkoutSubmit": "Order with a fee."
|
||||
}
|
||||
|
|
@ -1019,23 +1019,29 @@ abstract class AppLocalizations {
|
|||
/// **'This feature is not available in your current plan.'**
|
||||
String get errorPlanNotAllowed;
|
||||
|
||||
/// No description provided for @errorPlanUpgradeNotYearly.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'The plan upgrade must be paid for annually, as the current plan is also billed annually.'**
|
||||
String get errorPlanUpgradeNotYearly;
|
||||
|
||||
/// No description provided for @upgradeToPaidPlan.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Upgrade to a paid plan.'**
|
||||
String get upgradeToPaidPlan;
|
||||
|
||||
/// No description provided for @proYearlyPrice.
|
||||
/// No description provided for @year.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'10€/year'**
|
||||
String get proYearlyPrice;
|
||||
/// **'year'**
|
||||
String get year;
|
||||
|
||||
/// No description provided for @proMonthlyPrice.
|
||||
/// No description provided for @month.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'1€/month'**
|
||||
String get proMonthlyPrice;
|
||||
/// **'month'**
|
||||
String get month;
|
||||
|
||||
/// No description provided for @proFeature1.
|
||||
///
|
||||
|
|
@ -1055,18 +1061,6 @@ abstract class AppLocalizations {
|
|||
/// **'3 additional Free users'**
|
||||
String get proFeature3;
|
||||
|
||||
/// No description provided for @familyYearlyPrice.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'20€/year'**
|
||||
String get familyYearlyPrice;
|
||||
|
||||
/// No description provided for @familyMonthlyPrice.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'2€/month'**
|
||||
String get familyMonthlyPrice;
|
||||
|
||||
/// No description provided for @familyFeature1.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
@ -1109,6 +1103,18 @@ abstract class AppLocalizations {
|
|||
/// **'Your transaction history'**
|
||||
String get transactionHistory;
|
||||
|
||||
/// No description provided for @manageSubscription.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Manage your subscription'**
|
||||
String get manageSubscription;
|
||||
|
||||
/// No description provided for @nextPayment.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Next payment'**
|
||||
String get nextPayment;
|
||||
|
||||
/// No description provided for @currentBalance.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
@ -1234,6 +1240,72 @@ abstract class AppLocalizations {
|
|||
/// In en, this message translates to:
|
||||
/// **'Voucher redeemed'**
|
||||
String get transactionVoucherRedeemed;
|
||||
|
||||
/// No description provided for @checkoutOptions.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Options'**
|
||||
String get checkoutOptions;
|
||||
|
||||
/// No description provided for @checkoutPayYearly.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Pay yearly'**
|
||||
String get checkoutPayYearly;
|
||||
|
||||
/// No description provided for @checkoutTotal.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Total'**
|
||||
String get checkoutTotal;
|
||||
|
||||
/// No description provided for @selectPaymentMethode.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Select Payment Method'**
|
||||
String get selectPaymentMethode;
|
||||
|
||||
/// No description provided for @twonlyCredit.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'twonly-Credit'**
|
||||
String get twonlyCredit;
|
||||
|
||||
/// No description provided for @notEnoughCredit.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'You do not have enough credit!'**
|
||||
String get notEnoughCredit;
|
||||
|
||||
/// No description provided for @chargeCredit.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Charge credit'**
|
||||
String get chargeCredit;
|
||||
|
||||
/// No description provided for @autoRenewal.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Auto renewal'**
|
||||
String get autoRenewal;
|
||||
|
||||
/// No description provided for @autoRenewalDesc.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'You can change this at any time.'**
|
||||
String get autoRenewalDesc;
|
||||
|
||||
/// No description provided for @planSuccessUpgraded.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Successfully upgraded your plan.'**
|
||||
String get planSuccessUpgraded;
|
||||
|
||||
/// No description provided for @checkoutSubmit.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Order with a fee.'**
|
||||
String get checkoutSubmit;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||
|
|
|
|||
|
|
@ -482,14 +482,17 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get errorPlanNotAllowed => 'Dieses Feature ist in deinem aktuellen Plan nicht verfügbar.';
|
||||
|
||||
@override
|
||||
String get errorPlanUpgradeNotYearly => 'Das Upgrade des Plans muss jährlich bezahlt werden, da der aktuelle Plan ebenfalls jährlich abgerechnet wird.';
|
||||
|
||||
@override
|
||||
String get upgradeToPaidPlan => 'Upgrade auf einen kostenpflichtigen Plan.';
|
||||
|
||||
@override
|
||||
String get proYearlyPrice => '10€/Jahr';
|
||||
String get year => 'year';
|
||||
|
||||
@override
|
||||
String get proMonthlyPrice => '1€/Monat';
|
||||
String get month => 'month';
|
||||
|
||||
@override
|
||||
String get proFeature1 => '✓ Unbegrenzte Medien-Datei-Uploads';
|
||||
|
|
@ -500,12 +503,6 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get proFeature3 => '3 zusätzliche kostenlose Benutzer';
|
||||
|
||||
@override
|
||||
String get familyYearlyPrice => '20€/Jahr';
|
||||
|
||||
@override
|
||||
String get familyMonthlyPrice => '2€/Monat';
|
||||
|
||||
@override
|
||||
String get familyFeature1 => '✓ Unbegrenzte Medien-Datei-Uploads';
|
||||
|
||||
|
|
@ -528,7 +525,13 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
String get transactionHistory => 'Transaktionshistorie';
|
||||
|
||||
@override
|
||||
String get currentBalance => 'Aktueller Kontostand';
|
||||
String get manageSubscription => 'Manage your subscription';
|
||||
|
||||
@override
|
||||
String get nextPayment => 'Next payment';
|
||||
|
||||
@override
|
||||
String get currentBalance => 'Dein Guthaben';
|
||||
|
||||
@override
|
||||
String get manageAdditionalUsers => 'Zusätzlichen Benutzer verwalten';
|
||||
|
|
@ -589,4 +592,37 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get transactionVoucherRedeemed => 'Gutschein eingelöst';
|
||||
|
||||
@override
|
||||
String get checkoutOptions => 'Optionen';
|
||||
|
||||
@override
|
||||
String get checkoutPayYearly => 'Jährlich bezahlen';
|
||||
|
||||
@override
|
||||
String get checkoutTotal => 'Gesamt';
|
||||
|
||||
@override
|
||||
String get selectPaymentMethode => 'Zahlungsmethode auswählen';
|
||||
|
||||
@override
|
||||
String get twonlyCredit => 'twonly-Guthaben';
|
||||
|
||||
@override
|
||||
String get notEnoughCredit => 'Du hast nicht genügend Guthaben!';
|
||||
|
||||
@override
|
||||
String get chargeCredit => 'Guthaben aufladen';
|
||||
|
||||
@override
|
||||
String get autoRenewal => 'Automatische Verlängerung';
|
||||
|
||||
@override
|
||||
String get autoRenewalDesc => 'Du kannst dies jederzeit ändern.';
|
||||
|
||||
@override
|
||||
String get planSuccessUpgraded => 'Dein Plan wurde erfolgreich aktualisiert.';
|
||||
|
||||
@override
|
||||
String get checkoutSubmit => 'Kostenpflichtig bestellen';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -482,14 +482,17 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get errorPlanNotAllowed => 'This feature is not available in your current plan.';
|
||||
|
||||
@override
|
||||
String get errorPlanUpgradeNotYearly => 'The plan upgrade must be paid for annually, as the current plan is also billed annually.';
|
||||
|
||||
@override
|
||||
String get upgradeToPaidPlan => 'Upgrade to a paid plan.';
|
||||
|
||||
@override
|
||||
String get proYearlyPrice => '10€/year';
|
||||
String get year => 'year';
|
||||
|
||||
@override
|
||||
String get proMonthlyPrice => '1€/month';
|
||||
String get month => 'month';
|
||||
|
||||
@override
|
||||
String get proFeature1 => '✓ Unlimited media file uploads';
|
||||
|
|
@ -500,12 +503,6 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get proFeature3 => '3 additional Free users';
|
||||
|
||||
@override
|
||||
String get familyYearlyPrice => '20€/year';
|
||||
|
||||
@override
|
||||
String get familyMonthlyPrice => '2€/month';
|
||||
|
||||
@override
|
||||
String get familyFeature1 => '✓ Unlimited media file uploads';
|
||||
|
||||
|
|
@ -527,6 +524,12 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get transactionHistory => 'Your transaction history';
|
||||
|
||||
@override
|
||||
String get manageSubscription => 'Manage your subscription';
|
||||
|
||||
@override
|
||||
String get nextPayment => 'Next payment';
|
||||
|
||||
@override
|
||||
String get currentBalance => 'Current balance';
|
||||
|
||||
|
|
@ -589,4 +592,37 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get transactionVoucherRedeemed => 'Voucher redeemed';
|
||||
|
||||
@override
|
||||
String get checkoutOptions => 'Options';
|
||||
|
||||
@override
|
||||
String get checkoutPayYearly => 'Pay yearly';
|
||||
|
||||
@override
|
||||
String get checkoutTotal => 'Total';
|
||||
|
||||
@override
|
||||
String get selectPaymentMethode => 'Select Payment Method';
|
||||
|
||||
@override
|
||||
String get twonlyCredit => 'twonly-Credit';
|
||||
|
||||
@override
|
||||
String get notEnoughCredit => 'You do not have enough credit!';
|
||||
|
||||
@override
|
||||
String get chargeCredit => 'Charge credit';
|
||||
|
||||
@override
|
||||
String get autoRenewal => 'Auto renewal';
|
||||
|
||||
@override
|
||||
String get autoRenewalDesc => 'You can change this at any time.';
|
||||
|
||||
@override
|
||||
String get planSuccessUpgraded => 'Successfully upgraded your plan.';
|
||||
|
||||
@override
|
||||
String get checkoutSubmit => 'Order with a fee.';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -919,6 +919,7 @@ class ApplicationData_SwitchToPayedPlan extends $pb.GeneratedMessage {
|
|||
factory ApplicationData_SwitchToPayedPlan({
|
||||
$core.String? planId,
|
||||
$core.bool? payMonthly,
|
||||
$core.bool? autoRenewal,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (planId != null) {
|
||||
|
|
@ -927,6 +928,9 @@ class ApplicationData_SwitchToPayedPlan extends $pb.GeneratedMessage {
|
|||
if (payMonthly != null) {
|
||||
$result.payMonthly = payMonthly;
|
||||
}
|
||||
if (autoRenewal != null) {
|
||||
$result.autoRenewal = autoRenewal;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
ApplicationData_SwitchToPayedPlan._() : super();
|
||||
|
|
@ -936,6 +940,7 @@ class ApplicationData_SwitchToPayedPlan extends $pb.GeneratedMessage {
|
|||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ApplicationData.SwitchToPayedPlan', package: const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'), createEmptyInstance: create)
|
||||
..aOS(1, _omitFieldNames ? '' : 'planId')
|
||||
..aOB(2, _omitFieldNames ? '' : 'payMonthly')
|
||||
..aOB(3, _omitFieldNames ? '' : 'autoRenewal')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
|
|
@ -977,6 +982,15 @@ class ApplicationData_SwitchToPayedPlan extends $pb.GeneratedMessage {
|
|||
$core.bool hasPayMonthly() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearPayMonthly() => clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool get autoRenewal => $_getBF(2);
|
||||
@$pb.TagNumber(3)
|
||||
set autoRenewal($core.bool v) { $_setBool(2, v); }
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasAutoRenewal() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearAutoRenewal() => clearField(3);
|
||||
}
|
||||
|
||||
class ApplicationData_CreateVoucher extends $pb.GeneratedMessage {
|
||||
|
|
|
|||
|
|
@ -211,6 +211,7 @@ const ApplicationData_SwitchToPayedPlan$json = {
|
|||
'2': [
|
||||
{'1': 'plan_id', '3': 1, '4': 1, '5': 9, '10': 'planId'},
|
||||
{'1': 'pay_monthly', '3': 2, '4': 1, '5': 8, '10': 'payMonthly'},
|
||||
{'1': 'auto_renewal', '3': 3, '4': 1, '5': 8, '10': 'autoRenewal'},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
@ -330,19 +331,19 @@ final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode(
|
|||
'F0YYgBAUIMCgpfcHVzaF9kYXRhGi8KEUdldFVzZXJCeVVzZXJuYW1lEhoKCHVzZXJuYW1lGAEg'
|
||||
'ASgJUgh1c2VybmFtZRo1ChRVcGRhdGVHb29nbGVGY21Ub2tlbhIdCgpnb29nbGVfZmNtGAEgAS'
|
||||
'gJUglnb29nbGVGY20aJgoLR2V0VXNlckJ5SWQSFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkGikK'
|
||||
'DVJlZGVlbVZvdWNoZXISGAoHdm91Y2hlchgBIAEoCVIHdm91Y2hlchpNChFTd2l0Y2hUb1BheW'
|
||||
'DVJlZGVlbVZvdWNoZXISGAoHdm91Y2hlchgBIAEoCVIHdm91Y2hlchpwChFTd2l0Y2hUb1BheW'
|
||||
'VkUGxhbhIXCgdwbGFuX2lkGAEgASgJUgZwbGFuSWQSHwoLcGF5X21vbnRobHkYAiABKAhSCnBh'
|
||||
'eU1vbnRobHkaMAoNQ3JlYXRlVm91Y2hlchIfCgt2YWx1ZV9jZW50cxgBIAEoDVIKdmFsdWVDZW'
|
||||
'50cxoNCgtHZXRMb2NhdGlvbhoNCgtHZXRWb3VjaGVycxoTChFHZXRBdmFpbGFibGVQbGFucxoX'
|
||||
'ChVHZXRBZGRBY2NvdW50c0ludml0ZXMaFQoTR2V0Q3VycmVudFBsYW5JbmZvcxo3ChRSZWRlZW'
|
||||
'1BZGRpdGlvbmFsQ29kZRIfCgtpbnZpdGVfY29kZRgCIAEoCVIKaW52aXRlQ29kZRotChJHZXRQ'
|
||||
'cmVrZXlzQnlVc2VySWQSFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkGjsKDkdldFVwbG9hZFRva2'
|
||||
'VuEikKEHJlY2lwaWVudHNfY291bnQYASABKA1SD3JlY2lwaWVudHNDb3VudBqJAQoKVXBsb2Fk'
|
||||
'RGF0YRIhCgx1cGxvYWRfdG9rZW4YASABKAxSC3VwbG9hZFRva2VuEhYKBm9mZnNldBgCIAEoDV'
|
||||
'IGb2Zmc2V0EhIKBGRhdGEYAyABKAxSBGRhdGESHwoIY2hlY2tzdW0YBCABKAxIAFIIY2hlY2tz'
|
||||
'dW2IAQFCCwoJX2NoZWNrc3VtGk0KDERvd25sb2FkRGF0YRIlCg5kb3dubG9hZF90b2tlbhgBIA'
|
||||
'EoDFINZG93bmxvYWRUb2tlbhIWCgZvZmZzZXQYAiABKA1SBm9mZnNldEIRCg9BcHBsaWNhdGlv'
|
||||
'bkRhdGE=');
|
||||
'eU1vbnRobHkSIQoMYXV0b19yZW5ld2FsGAMgASgIUgthdXRvUmVuZXdhbBowCg1DcmVhdGVWb3'
|
||||
'VjaGVyEh8KC3ZhbHVlX2NlbnRzGAEgASgNUgp2YWx1ZUNlbnRzGg0KC0dldExvY2F0aW9uGg0K'
|
||||
'C0dldFZvdWNoZXJzGhMKEUdldEF2YWlsYWJsZVBsYW5zGhcKFUdldEFkZEFjY291bnRzSW52aX'
|
||||
'RlcxoVChNHZXRDdXJyZW50UGxhbkluZm9zGjcKFFJlZGVlbUFkZGl0aW9uYWxDb2RlEh8KC2lu'
|
||||
'dml0ZV9jb2RlGAIgASgJUgppbnZpdGVDb2RlGi0KEkdldFByZWtleXNCeVVzZXJJZBIXCgd1c2'
|
||||
'VyX2lkGAEgASgDUgZ1c2VySWQaOwoOR2V0VXBsb2FkVG9rZW4SKQoQcmVjaXBpZW50c19jb3Vu'
|
||||
'dBgBIAEoDVIPcmVjaXBpZW50c0NvdW50GokBCgpVcGxvYWREYXRhEiEKDHVwbG9hZF90b2tlbh'
|
||||
'gBIAEoDFILdXBsb2FkVG9rZW4SFgoGb2Zmc2V0GAIgASgNUgZvZmZzZXQSEgoEZGF0YRgDIAEo'
|
||||
'DFIEZGF0YRIfCghjaGVja3N1bRgEIAEoDEgAUghjaGVja3N1bYgBAUILCglfY2hlY2tzdW0aTQ'
|
||||
'oMRG93bmxvYWREYXRhEiUKDmRvd25sb2FkX3Rva2VuGAEgASgMUg1kb3dubG9hZFRva2VuEhYK'
|
||||
'Bm9mZnNldBgCIAEoDVIGb2Zmc2V0QhEKD0FwcGxpY2F0aW9uRGF0YQ==');
|
||||
|
||||
@$core.Deprecated('Use responseDescriptor instead')
|
||||
const Response$json = {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ class ErrorCode extends $pb.ProtobufEnum {
|
|||
static const ErrorCode PlanLimitReached = ErrorCode._(1023, _omitEnumNames ? '' : 'PlanLimitReached');
|
||||
static const ErrorCode NotEnoughCredit = ErrorCode._(1024, _omitEnumNames ? '' : 'NotEnoughCredit');
|
||||
static const ErrorCode PlanDowngrade = ErrorCode._(1025, _omitEnumNames ? '' : 'PlanDowngrade');
|
||||
static const ErrorCode PlanUpgradeNotYearly = ErrorCode._(1026, _omitEnumNames ? '' : 'PlanUpgradeNotYearly');
|
||||
|
||||
static const $core.List<ErrorCode> values = <ErrorCode> [
|
||||
Unknown,
|
||||
|
|
@ -72,6 +73,7 @@ class ErrorCode extends $pb.ProtobufEnum {
|
|||
PlanLimitReached,
|
||||
NotEnoughCredit,
|
||||
PlanDowngrade,
|
||||
PlanUpgradeNotYearly,
|
||||
];
|
||||
|
||||
static final $core.Map<$core.int, ErrorCode> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ const ErrorCode$json = {
|
|||
{'1': 'PlanLimitReached', '2': 1023},
|
||||
{'1': 'NotEnoughCredit', '2': 1024},
|
||||
{'1': 'PlanDowngrade', '2': 1025},
|
||||
{'1': 'PlanUpgradeNotYearly', '2': 1026},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
@ -62,5 +63,5 @@ final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode(
|
|||
'4Q+QcSGAoTQXBpRW5kcG9pbnROb3RGb3VuZBD6BxIWChFBdXRoVG9rZW5Ob3RWYWxpZBD7BxIT'
|
||||
'Cg5JbnZhbGlkUHJlS2V5cxD8BxITCg5Wb3VjaGVySW5WYWxpZBD9BxITCg5QbGFuTm90QWxsb3'
|
||||
'dlZBD+BxIVChBQbGFuTGltaXRSZWFjaGVkEP8HEhQKD05vdEVub3VnaENyZWRpdBCACBISCg1Q'
|
||||
'bGFuRG93bmdyYWRlEIEI');
|
||||
'bGFuRG93bmdyYWRlEIEIEhkKFFBsYW5VcGdyYWRlTm90WWVhcmx5EIII');
|
||||
|
||||
|
|
|
|||
|
|
@ -440,6 +440,17 @@ class ApiProvider {
|
|||
return await sendRequestSync(req);
|
||||
}
|
||||
|
||||
Future<Result> switchToPayedPlan(
|
||||
String planId, bool payMonthly, bool autoRenewal) async {
|
||||
var get = ApplicationData_SwitchToPayedPlan()
|
||||
..planId = planId
|
||||
..payMonthly = payMonthly
|
||||
..autoRenewal = autoRenewal;
|
||||
var appData = ApplicationData()..switchtopayedplan = get;
|
||||
var req = createClientToServerFromApplicationData(appData);
|
||||
return await sendRequestSync(req);
|
||||
}
|
||||
|
||||
Future<Result> redeemVoucher(String voucher) async {
|
||||
var get = ApplicationData_RedeemVoucher()..voucher = voucher;
|
||||
var appData = ApplicationData()..redeemvoucher = get;
|
||||
|
|
|
|||
|
|
@ -85,6 +85,8 @@ String errorCodeToText(BuildContext context, ErrorCode code) {
|
|||
return context.lang.errorPlanNotAllowed;
|
||||
case ErrorCode.VoucherInValid:
|
||||
return context.lang.errorVoucherInvalid;
|
||||
case ErrorCode.PlanUpgradeNotYearly:
|
||||
return context.lang.errorPlanUpgradeNotYearly;
|
||||
default:
|
||||
return code.toString(); // Fallback for unrecognized keys
|
||||
}
|
||||
|
|
|
|||
114
lib/src/views/settings/subscription/checkout_view.dart
Normal file
114
lib/src/views/settings/subscription/checkout_view.dart
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/settings/subscription/select_payment.dart';
|
||||
import 'package:twonly/src/views/settings/subscription/subscription_view.dart';
|
||||
|
||||
class CheckoutView extends StatefulWidget {
|
||||
const CheckoutView({
|
||||
super.key,
|
||||
required this.planId,
|
||||
});
|
||||
|
||||
final String planId;
|
||||
|
||||
@override
|
||||
State<CheckoutView> createState() => _CheckoutViewState();
|
||||
}
|
||||
|
||||
class _CheckoutViewState extends State<CheckoutView> {
|
||||
int checkoutInCents = 0;
|
||||
bool paidMonthly = false;
|
||||
bool tryAutoRenewal = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
setCheckout(true);
|
||||
}
|
||||
|
||||
void setCheckout(bool init) {
|
||||
checkoutInCents = getPlanPrice(widget.planId, paidMonthly);
|
||||
if (!init) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String totalPrice =
|
||||
"${localePrizing(context, checkoutInCents)}/${(paidMonthly) ? context.lang.month : context.lang.year}";
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(context.lang.checkoutOptions),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
PlanCard(planId: widget.planId),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: ListTile(
|
||||
title: Text(context.lang.checkoutPayYearly),
|
||||
onTap: () {
|
||||
paidMonthly = !paidMonthly;
|
||||
setCheckout(false);
|
||||
},
|
||||
trailing: Checkbox(
|
||||
value: !paidMonthly,
|
||||
onChanged: (a) {
|
||||
paidMonthly = !paidMonthly;
|
||||
setCheckout(false);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Text(
|
||||
context.lang.checkoutTotal,
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(
|
||||
totalPrice,
|
||||
textAlign: TextAlign.end,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: FilledButton(
|
||||
onPressed: () async {
|
||||
bool? success = await Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) {
|
||||
return SelectPaymentView(
|
||||
planId: widget.planId, payMonthly: paidMonthly);
|
||||
}));
|
||||
if (success != null && success && context.mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: Text(context.lang.selectPaymentMethode)),
|
||||
),
|
||||
SizedBox(height: 20)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
251
lib/src/views/settings/subscription/select_payment.dart
Normal file
251
lib/src/views/settings/subscription/select_payment.dart
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/providers/connection_provider.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
import 'package:twonly/src/views/settings/subscription/subscription_view.dart';
|
||||
import 'package:twonly/src/views/settings/subscription/voucher_view.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class SelectPaymentView extends StatefulWidget {
|
||||
const SelectPaymentView({
|
||||
super.key,
|
||||
this.planId,
|
||||
this.payMonthly,
|
||||
this.valueInCents,
|
||||
});
|
||||
|
||||
final String? planId;
|
||||
final bool? payMonthly;
|
||||
final int? valueInCents;
|
||||
|
||||
@override
|
||||
State<SelectPaymentView> createState() => _SelectPaymentViewState();
|
||||
}
|
||||
|
||||
enum PaymentMethods {
|
||||
twonlyCredit,
|
||||
googleSubscription,
|
||||
appleSubscription,
|
||||
}
|
||||
|
||||
class _SelectPaymentViewState extends State<SelectPaymentView> {
|
||||
int? ballanceInCents;
|
||||
int checkoutInCents = 0;
|
||||
bool tryAutoRenewal = true;
|
||||
|
||||
PaymentMethods paymentMethods = PaymentMethods.twonlyCredit;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
setCheckout(true);
|
||||
initAsync();
|
||||
}
|
||||
|
||||
Future initAsync() async {
|
||||
final ballance = await loadPlanBallance();
|
||||
ballanceInCents =
|
||||
ballance!.transactions.map((a) => a.depositCents.toInt()).sum;
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void setCheckout(bool init) {
|
||||
if (widget.valueInCents != null && widget.valueInCents! > 0) {
|
||||
checkoutInCents = widget.valueInCents!;
|
||||
} else if (widget.planId != null) {
|
||||
checkoutInCents = getPlanPrice(widget.planId!, widget.payMonthly!);
|
||||
} else {
|
||||
/// Nothing to checkout for...
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
if (!init) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String totalPrice = (widget.planId != null && widget.payMonthly != null)
|
||||
? "${localePrizing(context, checkoutInCents)}/${(widget.payMonthly!) ? context.lang.month : context.lang.year}"
|
||||
: localePrizing(context, checkoutInCents);
|
||||
bool canPay = (paymentMethods == PaymentMethods.twonlyCredit &&
|
||||
(ballanceInCents == null || ballanceInCents! >= checkoutInCents));
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(context.lang.selectPaymentMethode),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(context.lang.twonlyCredit),
|
||||
if (ballanceInCents != null)
|
||||
Text(
|
||||
"${context.lang.currentBalance}: ${localePrizing(context, ballanceInCents!)}",
|
||||
style: TextStyle(fontSize: 10),
|
||||
)
|
||||
],
|
||||
),
|
||||
Checkbox(
|
||||
value:
|
||||
paymentMethods == PaymentMethods.twonlyCredit,
|
||||
onChanged: (bool? value) {
|
||||
setState(() {
|
||||
paymentMethods = PaymentMethods.twonlyCredit;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (!canPay) ...[
|
||||
Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Text(
|
||||
context.lang.notEnoughCredit,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: FilledButton(
|
||||
onPressed: () async {
|
||||
await Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) {
|
||||
return VoucherView();
|
||||
}));
|
||||
initAsync();
|
||||
},
|
||||
child: Text(context.lang.chargeCredit),
|
||||
),
|
||||
),
|
||||
],
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: ListTile(
|
||||
title: Text(context.lang.autoRenewal),
|
||||
subtitle: Text(context.lang.autoRenewalDesc),
|
||||
onTap: () {
|
||||
tryAutoRenewal = !tryAutoRenewal;
|
||||
setCheckout(false);
|
||||
},
|
||||
trailing: Checkbox(
|
||||
value: tryAutoRenewal,
|
||||
onChanged: (a) {
|
||||
tryAutoRenewal = !tryAutoRenewal;
|
||||
setCheckout(false);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Text(
|
||||
context.lang.checkoutTotal,
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(
|
||||
totalPrice,
|
||||
textAlign: TextAlign.end,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: FilledButton(
|
||||
onPressed: (canPay)
|
||||
? () async {
|
||||
final res = await apiProvider.switchToPayedPlan(
|
||||
widget.planId!, widget.payMonthly!, tryAutoRenewal);
|
||||
if (!context.mounted) return;
|
||||
if (res.isSuccess) {
|
||||
context.read<CustomChangeProvider>().plan =
|
||||
widget.planId!;
|
||||
var user = await getUser();
|
||||
if (user != null) {
|
||||
user.subscriptionPlan = widget.planId!;
|
||||
await updateUser(user);
|
||||
}
|
||||
context
|
||||
.read<CustomChangeProvider>()
|
||||
.updatePlan(widget.planId!);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content:
|
||||
Text(context.lang.planSuccessUpgraded)),
|
||||
);
|
||||
Navigator.of(context).pop(true);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
errorCodeToText(context, res.error),
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
child: Text(context.lang.checkoutSubmit),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () => launchUrl(Uri.parse(
|
||||
"https://twonly.eu/legal/de/#revocation-policy")),
|
||||
child: Text(
|
||||
"Widerrufsbelehrung",
|
||||
style: TextStyle(color: Colors.blue),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => launchUrl(
|
||||
Uri.parse("https://twonly.eu/legal/de/agb.html")),
|
||||
child: Text(
|
||||
"ABG",
|
||||
style: TextStyle(color: Colors.blue),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 20)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,9 +10,47 @@ import 'package:twonly/src/providers/connection_provider.dart';
|
|||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
import 'package:twonly/src/views/components/better_list_title.dart';
|
||||
import 'package:twonly/src/views/settings/subscription/checkout_view.dart';
|
||||
import 'package:twonly/src/views/settings/subscription/transaction_view.dart';
|
||||
import 'package:twonly/src/views/settings/subscription/voucher_view.dart';
|
||||
|
||||
String localePrizing(BuildContext context, int cents) {
|
||||
Locale myLocale = Localizations.localeOf(context);
|
||||
|
||||
double euros = cents / 100;
|
||||
|
||||
if (euros == euros.toInt()) {
|
||||
return "${euros.toInt()}€";
|
||||
}
|
||||
|
||||
return NumberFormat.currency(
|
||||
locale: myLocale.toString(),
|
||||
symbol: '€',
|
||||
decimalDigits: 2,
|
||||
).format(cents / 100);
|
||||
}
|
||||
|
||||
Future<Response_PlanBallance?> loadPlanBallance() async {
|
||||
Response_PlanBallance? ballance;
|
||||
final user = await getUser();
|
||||
if (user == null) return ballance;
|
||||
ballance = await apiProvider.getPlanBallance();
|
||||
if (ballance != null) {
|
||||
user.lastPlanBallance = ballance.writeToJson();
|
||||
await updateUser(user);
|
||||
} else if (user.lastPlanBallance != null) {
|
||||
try {
|
||||
ballance = Response_PlanBallance.fromJson(
|
||||
user.lastPlanBallance!,
|
||||
);
|
||||
} catch (e) {
|
||||
Logger("subscription_view.dart").shout("from json: $e");
|
||||
}
|
||||
}
|
||||
|
||||
return ballance;
|
||||
}
|
||||
|
||||
class SubscriptionView extends StatefulWidget {
|
||||
const SubscriptionView({super.key});
|
||||
|
||||
|
|
@ -31,21 +69,7 @@ class _SubscriptionViewState extends State<SubscriptionView> {
|
|||
}
|
||||
|
||||
Future initAsync() async {
|
||||
final user = await getUser();
|
||||
if (user == null) return;
|
||||
ballance = await apiProvider.getPlanBallance();
|
||||
if (ballance != null) {
|
||||
user.lastPlanBallance = ballance!.writeToJson();
|
||||
await updateUser(user);
|
||||
} else if (user.lastPlanBallance != null) {
|
||||
try {
|
||||
ballance = Response_PlanBallance.fromJson(
|
||||
user.lastPlanBallance!,
|
||||
);
|
||||
} catch (e) {
|
||||
Logger("subscription_view.dart").shout("from json: $e");
|
||||
}
|
||||
}
|
||||
ballance = await loadPlanBallance();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
|
|
@ -110,25 +134,25 @@ class _SubscriptionViewState extends State<SubscriptionView> {
|
|||
),
|
||||
if (currentPlan != "Family" && currentPlan != "Pro")
|
||||
PlanCard(
|
||||
title: "Pro",
|
||||
yearlyPrice: context.lang.proYearlyPrice,
|
||||
monthlyPrice: context.lang.proMonthlyPrice,
|
||||
features: [
|
||||
context.lang.proFeature1,
|
||||
context.lang.proFeature2,
|
||||
context.lang.proFeature3,
|
||||
],
|
||||
planId: "Pro",
|
||||
onTap: () async {
|
||||
await Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) {
|
||||
return CheckoutView(planId: "Pro");
|
||||
}));
|
||||
initAsync();
|
||||
},
|
||||
),
|
||||
if (currentPlan != "Family")
|
||||
PlanCard(
|
||||
title: "Family",
|
||||
yearlyPrice: context.lang.familyYearlyPrice,
|
||||
monthlyPrice: context.lang.familyMonthlyPrice,
|
||||
features: [
|
||||
context.lang.familyFeature1,
|
||||
context.lang.familyFeature2,
|
||||
context.lang.familyFeature3,
|
||||
],
|
||||
planId: "Family",
|
||||
onTap: () async {
|
||||
await Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) {
|
||||
return CheckoutView(planId: "Family");
|
||||
}));
|
||||
initAsync();
|
||||
},
|
||||
),
|
||||
if (currentPlan == "Preview" || currentPlan == "Free") ...[
|
||||
SizedBox(height: 10),
|
||||
|
|
@ -145,31 +169,23 @@ class _SubscriptionViewState extends State<SubscriptionView> {
|
|||
SizedBox(height: 10),
|
||||
if (currentPlan != "Free")
|
||||
PlanCard(
|
||||
title: "Free",
|
||||
yearlyPrice: "",
|
||||
monthlyPrice: "",
|
||||
features: [
|
||||
context.lang.freeFeature1,
|
||||
],
|
||||
planId: "Free",
|
||||
onTap: () {},
|
||||
),
|
||||
PlanCard(
|
||||
title: "Plus",
|
||||
yearlyPrice: "",
|
||||
monthlyPrice: "",
|
||||
features: [
|
||||
context.lang.plusFeature1,
|
||||
],
|
||||
planId: "Plus",
|
||||
onTap: () {},
|
||||
),
|
||||
],
|
||||
SizedBox(height: 10),
|
||||
if (currentPlan != "Family") Divider(),
|
||||
if (currentPlan == "Family" || currentPlan == "Pro")
|
||||
BetterListTile(
|
||||
icon: FontAwesomeIcons.userPlus,
|
||||
text: "Manage your subscription",
|
||||
icon: FontAwesomeIcons.gears,
|
||||
text: context.lang.manageSubscription,
|
||||
subtitle: (nextPayment != null)
|
||||
? Text(
|
||||
"Next payment: ${DateFormat.yMMMMd(myLocale.toString()).format(nextPayment)}")
|
||||
"${context.lang.nextPayment}: ${DateFormat.yMMMMd(myLocale.toString()).format(nextPayment)}")
|
||||
: null,
|
||||
onTap: () {},
|
||||
),
|
||||
|
|
@ -214,81 +230,118 @@ class _SubscriptionViewState extends State<SubscriptionView> {
|
|||
}
|
||||
}
|
||||
|
||||
int getPlanPrice(String planId, bool paidMonthly) {
|
||||
switch (planId) {
|
||||
case "Pro":
|
||||
return (paidMonthly) ? 100 : 1000;
|
||||
case "Family":
|
||||
return (paidMonthly) ? 200 : 2000;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
class PlanCard extends StatelessWidget {
|
||||
final String title;
|
||||
final String yearlyPrice;
|
||||
final String monthlyPrice;
|
||||
final List<String> features;
|
||||
final String planId;
|
||||
final Function()? onTap;
|
||||
|
||||
const PlanCard({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.yearlyPrice,
|
||||
required this.monthlyPrice,
|
||||
required this.features,
|
||||
required this.planId,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
int yearlyPrice = getPlanPrice(planId, false);
|
||||
int monthlyPrice = getPlanPrice(planId, true);
|
||||
List<String> features = [];
|
||||
|
||||
switch (planId) {
|
||||
case "Free":
|
||||
features = [context.lang.freeFeature1];
|
||||
break;
|
||||
case "Plus":
|
||||
features = [context.lang.plusFeature1];
|
||||
break;
|
||||
case "Pro":
|
||||
features = [
|
||||
context.lang.proFeature1,
|
||||
context.lang.proFeature2,
|
||||
context.lang.proFeature3
|
||||
];
|
||||
break;
|
||||
case "Family":
|
||||
features = [
|
||||
context.lang.familyFeature1,
|
||||
context.lang.familyFeature2,
|
||||
context.lang.familyFeature3
|
||||
];
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16, right: 16),
|
||||
child: Card(
|
||||
elevation: 4,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Card(
|
||||
elevation: 4,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Text(
|
||||
planId,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
if (yearlyPrice != 0) SizedBox(height: 10),
|
||||
if (yearlyPrice != 0)
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
"${localePrizing(context, yearlyPrice)}/${context.lang.year}",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${localePrizing(context, monthlyPrice)}/${context.lang.month}",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
...features.map(
|
||||
(feature) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2.0),
|
||||
child: Text(
|
||||
feature,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
if (yearlyPrice != "") SizedBox(height: 10),
|
||||
if (yearlyPrice != "")
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
yearlyPrice,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
monthlyPrice,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
...features.map(
|
||||
(feature) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2.0),
|
||||
child: Text(
|
||||
feature,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -14,17 +14,6 @@ class TransactionView extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _TransactionViewState extends State<TransactionView> {
|
||||
// String typeToText(String type) {
|
||||
|
||||
// switch (type) {
|
||||
// case "VoucherCreated":
|
||||
|
||||
// break;
|
||||
// default:
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
|
|
|||
Loading…
Reference in a new issue