mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 18:28:40 +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.",
|
"errorPlanLimitReached": "Du hast das Limit deines Plans erreicht. Bitte upgrade deinen Plan.",
|
||||||
"errorPlanNotAllowed": "Dieses Feature ist in deinem aktuellen Plan nicht verfügbar.",
|
"errorPlanNotAllowed": "Dieses Feature ist in deinem aktuellen Plan nicht verfügbar.",
|
||||||
"errorVoucherInvalid": "Der eingegebene Gutschein-Code ist nicht gültig.",
|
"errorVoucherInvalid": "Der eingegebene Gutschein-Code ist nicht gültig.",
|
||||||
"proYearlyPrice": "10€/Jahr",
|
"errorPlanUpgradeNotYearly": "Das Upgrade des Plans muss jährlich bezahlt werden, da der aktuelle Plan ebenfalls jährlich abgerechnet wird.",
|
||||||
"proMonthlyPrice": "1€/Monat",
|
|
||||||
"proFeature1": "✓ Unbegrenzte Medien-Datei-Uploads",
|
"proFeature1": "✓ Unbegrenzte Medien-Datei-Uploads",
|
||||||
"proFeature2": "1 zusätzlicher Plus Benutzer",
|
"proFeature2": "1 zusätzlicher Plus Benutzer",
|
||||||
"proFeature3": "3 zusätzliche kostenlose Benutzer",
|
"proFeature3": "3 zusätzliche kostenlose Benutzer",
|
||||||
"familyYearlyPrice": "20€/Jahr",
|
"year": "year",
|
||||||
"familyMonthlyPrice": "2€/Monat",
|
"month": "month",
|
||||||
"familyFeature1": "✓ Unbegrenzte Medien-Datei-Uploads",
|
"familyFeature1": "✓ Unbegrenzte Medien-Datei-Uploads",
|
||||||
"familyFeature2": "4 zusätzliche Plus Benutzer",
|
"familyFeature2": "4 zusätzliche Plus Benutzer",
|
||||||
"familyFeature3": "5 zusätzliche kostenlose Benutzer",
|
"familyFeature3": "5 zusätzliche kostenlose Benutzer",
|
||||||
|
|
@ -183,7 +182,7 @@
|
||||||
"freeFeature1": "3 Medien-Datei-Uploads pro Tag",
|
"freeFeature1": "3 Medien-Datei-Uploads pro Tag",
|
||||||
"plusFeature1": "✓ Unbegrenzte Medien-Datei-Uploads",
|
"plusFeature1": "✓ Unbegrenzte Medien-Datei-Uploads",
|
||||||
"transactionHistory": "Transaktionshistorie",
|
"transactionHistory": "Transaktionshistorie",
|
||||||
"currentBalance": "Aktueller Kontostand",
|
"currentBalance": "Dein Guthaben",
|
||||||
"manageAdditionalUsers": "Zusätzlichen Benutzer verwalten",
|
"manageAdditionalUsers": "Zusätzlichen Benutzer verwalten",
|
||||||
"open": "Offene",
|
"open": "Offene",
|
||||||
"buy": "Kaufen",
|
"buy": "Kaufen",
|
||||||
|
|
@ -203,5 +202,16 @@
|
||||||
"transactionThanksForTesting": "Danke fürs Testen",
|
"transactionThanksForTesting": "Danke fürs Testen",
|
||||||
"transactionUnknown": "Unbekannte Transaktion",
|
"transactionUnknown": "Unbekannte Transaktion",
|
||||||
"transactionVoucherCreated": "Gutschein erstellt",
|
"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.",
|
"errorVoucherInvalid": "The voucher code you entered is not valid.",
|
||||||
"errorPlanLimitReached": "You have reached your plans limit. Please upgrade your plan.",
|
"errorPlanLimitReached": "You have reached your plans limit. Please upgrade your plan.",
|
||||||
"errorPlanNotAllowed": "This feature is not available in your current 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.",
|
"upgradeToPaidPlan": "Upgrade to a paid plan.",
|
||||||
"proYearlyPrice": "10€/year",
|
"year": "year",
|
||||||
"proMonthlyPrice": "1€/month",
|
"month": "month",
|
||||||
"proFeature1": "✓ Unlimited media file uploads",
|
"proFeature1": "✓ Unlimited media file uploads",
|
||||||
"proFeature2": "1 additional Plus user",
|
"proFeature2": "1 additional Plus user",
|
||||||
"proFeature3": "3 additional Free users",
|
"proFeature3": "3 additional Free users",
|
||||||
"familyYearlyPrice": "20€/year",
|
|
||||||
"familyMonthlyPrice": "2€/month",
|
|
||||||
"familyFeature1": "✓ Unlimited media file uploads",
|
"familyFeature1": "✓ Unlimited media file uploads",
|
||||||
"familyFeature2": "4 additional Plus users",
|
"familyFeature2": "4 additional Plus users",
|
||||||
"familyFeature3": "5 additional Free users",
|
"familyFeature3": "5 additional Free users",
|
||||||
|
|
@ -341,6 +340,8 @@
|
||||||
"freeFeature1": "3 Media file uploads per day",
|
"freeFeature1": "3 Media file uploads per day",
|
||||||
"plusFeature1": "✓ Unlimited media file uploads",
|
"plusFeature1": "✓ Unlimited media file uploads",
|
||||||
"transactionHistory": "Your transaction history",
|
"transactionHistory": "Your transaction history",
|
||||||
|
"manageSubscription": "Manage your subscription",
|
||||||
|
"nextPayment": "Next payment",
|
||||||
"currentBalance": "Current balance",
|
"currentBalance": "Current balance",
|
||||||
"manageAdditionalUsers": "Manage your additional users",
|
"manageAdditionalUsers": "Manage your additional users",
|
||||||
"open": "Open",
|
"open": "Open",
|
||||||
|
|
@ -361,5 +362,17 @@
|
||||||
"transactionThanksForTesting": "Thank you for testing",
|
"transactionThanksForTesting": "Thank you for testing",
|
||||||
"transactionUnknown": "Unknown transaction",
|
"transactionUnknown": "Unknown transaction",
|
||||||
"transactionVoucherCreated": "Voucher created",
|
"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.'**
|
/// **'This feature is not available in your current plan.'**
|
||||||
String get errorPlanNotAllowed;
|
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.
|
/// No description provided for @upgradeToPaidPlan.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Upgrade to a paid plan.'**
|
/// **'Upgrade to a paid plan.'**
|
||||||
String get upgradeToPaidPlan;
|
String get upgradeToPaidPlan;
|
||||||
|
|
||||||
/// No description provided for @proYearlyPrice.
|
/// No description provided for @year.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'10€/year'**
|
/// **'year'**
|
||||||
String get proYearlyPrice;
|
String get year;
|
||||||
|
|
||||||
/// No description provided for @proMonthlyPrice.
|
/// No description provided for @month.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'1€/month'**
|
/// **'month'**
|
||||||
String get proMonthlyPrice;
|
String get month;
|
||||||
|
|
||||||
/// No description provided for @proFeature1.
|
/// No description provided for @proFeature1.
|
||||||
///
|
///
|
||||||
|
|
@ -1055,18 +1061,6 @@ abstract class AppLocalizations {
|
||||||
/// **'3 additional Free users'**
|
/// **'3 additional Free users'**
|
||||||
String get proFeature3;
|
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.
|
/// No description provided for @familyFeature1.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
@ -1109,6 +1103,18 @@ abstract class AppLocalizations {
|
||||||
/// **'Your transaction history'**
|
/// **'Your transaction history'**
|
||||||
String get transactionHistory;
|
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.
|
/// No description provided for @currentBalance.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
@ -1234,6 +1240,72 @@ abstract class AppLocalizations {
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Voucher redeemed'**
|
/// **'Voucher redeemed'**
|
||||||
String get transactionVoucherRedeemed;
|
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> {
|
class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||||
|
|
|
||||||
|
|
@ -482,14 +482,17 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get errorPlanNotAllowed => 'Dieses Feature ist in deinem aktuellen Plan nicht verfügbar.';
|
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
|
@override
|
||||||
String get upgradeToPaidPlan => 'Upgrade auf einen kostenpflichtigen Plan.';
|
String get upgradeToPaidPlan => 'Upgrade auf einen kostenpflichtigen Plan.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get proYearlyPrice => '10€/Jahr';
|
String get year => 'year';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get proMonthlyPrice => '1€/Monat';
|
String get month => 'month';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get proFeature1 => '✓ Unbegrenzte Medien-Datei-Uploads';
|
String get proFeature1 => '✓ Unbegrenzte Medien-Datei-Uploads';
|
||||||
|
|
@ -500,12 +503,6 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get proFeature3 => '3 zusätzliche kostenlose Benutzer';
|
String get proFeature3 => '3 zusätzliche kostenlose Benutzer';
|
||||||
|
|
||||||
@override
|
|
||||||
String get familyYearlyPrice => '20€/Jahr';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get familyMonthlyPrice => '2€/Monat';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get familyFeature1 => '✓ Unbegrenzte Medien-Datei-Uploads';
|
String get familyFeature1 => '✓ Unbegrenzte Medien-Datei-Uploads';
|
||||||
|
|
||||||
|
|
@ -528,7 +525,13 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String get transactionHistory => 'Transaktionshistorie';
|
String get transactionHistory => 'Transaktionshistorie';
|
||||||
|
|
||||||
@override
|
@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
|
@override
|
||||||
String get manageAdditionalUsers => 'Zusätzlichen Benutzer verwalten';
|
String get manageAdditionalUsers => 'Zusätzlichen Benutzer verwalten';
|
||||||
|
|
@ -589,4 +592,37 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get transactionVoucherRedeemed => 'Gutschein eingelöst';
|
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
|
@override
|
||||||
String get errorPlanNotAllowed => 'This feature is not available in your current plan.';
|
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
|
@override
|
||||||
String get upgradeToPaidPlan => 'Upgrade to a paid plan.';
|
String get upgradeToPaidPlan => 'Upgrade to a paid plan.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get proYearlyPrice => '10€/year';
|
String get year => 'year';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get proMonthlyPrice => '1€/month';
|
String get month => 'month';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get proFeature1 => '✓ Unlimited media file uploads';
|
String get proFeature1 => '✓ Unlimited media file uploads';
|
||||||
|
|
@ -500,12 +503,6 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get proFeature3 => '3 additional Free users';
|
String get proFeature3 => '3 additional Free users';
|
||||||
|
|
||||||
@override
|
|
||||||
String get familyYearlyPrice => '20€/year';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get familyMonthlyPrice => '2€/month';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get familyFeature1 => '✓ Unlimited media file uploads';
|
String get familyFeature1 => '✓ Unlimited media file uploads';
|
||||||
|
|
||||||
|
|
@ -527,6 +524,12 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get transactionHistory => 'Your transaction history';
|
String get transactionHistory => 'Your transaction history';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get manageSubscription => 'Manage your subscription';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get nextPayment => 'Next payment';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get currentBalance => 'Current balance';
|
String get currentBalance => 'Current balance';
|
||||||
|
|
||||||
|
|
@ -589,4 +592,37 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get transactionVoucherRedeemed => 'Voucher redeemed';
|
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({
|
factory ApplicationData_SwitchToPayedPlan({
|
||||||
$core.String? planId,
|
$core.String? planId,
|
||||||
$core.bool? payMonthly,
|
$core.bool? payMonthly,
|
||||||
|
$core.bool? autoRenewal,
|
||||||
}) {
|
}) {
|
||||||
final $result = create();
|
final $result = create();
|
||||||
if (planId != null) {
|
if (planId != null) {
|
||||||
|
|
@ -927,6 +928,9 @@ class ApplicationData_SwitchToPayedPlan extends $pb.GeneratedMessage {
|
||||||
if (payMonthly != null) {
|
if (payMonthly != null) {
|
||||||
$result.payMonthly = payMonthly;
|
$result.payMonthly = payMonthly;
|
||||||
}
|
}
|
||||||
|
if (autoRenewal != null) {
|
||||||
|
$result.autoRenewal = autoRenewal;
|
||||||
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
ApplicationData_SwitchToPayedPlan._() : super();
|
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)
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ApplicationData.SwitchToPayedPlan', package: const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'), createEmptyInstance: create)
|
||||||
..aOS(1, _omitFieldNames ? '' : 'planId')
|
..aOS(1, _omitFieldNames ? '' : 'planId')
|
||||||
..aOB(2, _omitFieldNames ? '' : 'payMonthly')
|
..aOB(2, _omitFieldNames ? '' : 'payMonthly')
|
||||||
|
..aOB(3, _omitFieldNames ? '' : 'autoRenewal')
|
||||||
..hasRequiredFields = false
|
..hasRequiredFields = false
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -977,6 +982,15 @@ class ApplicationData_SwitchToPayedPlan extends $pb.GeneratedMessage {
|
||||||
$core.bool hasPayMonthly() => $_has(1);
|
$core.bool hasPayMonthly() => $_has(1);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
void clearPayMonthly() => clearField(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 {
|
class ApplicationData_CreateVoucher extends $pb.GeneratedMessage {
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,7 @@ const ApplicationData_SwitchToPayedPlan$json = {
|
||||||
'2': [
|
'2': [
|
||||||
{'1': 'plan_id', '3': 1, '4': 1, '5': 9, '10': 'planId'},
|
{'1': 'plan_id', '3': 1, '4': 1, '5': 9, '10': 'planId'},
|
||||||
{'1': 'pay_monthly', '3': 2, '4': 1, '5': 8, '10': 'payMonthly'},
|
{'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'
|
'F0YYgBAUIMCgpfcHVzaF9kYXRhGi8KEUdldFVzZXJCeVVzZXJuYW1lEhoKCHVzZXJuYW1lGAEg'
|
||||||
'ASgJUgh1c2VybmFtZRo1ChRVcGRhdGVHb29nbGVGY21Ub2tlbhIdCgpnb29nbGVfZmNtGAEgAS'
|
'ASgJUgh1c2VybmFtZRo1ChRVcGRhdGVHb29nbGVGY21Ub2tlbhIdCgpnb29nbGVfZmNtGAEgAS'
|
||||||
'gJUglnb29nbGVGY20aJgoLR2V0VXNlckJ5SWQSFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkGikK'
|
'gJUglnb29nbGVGY20aJgoLR2V0VXNlckJ5SWQSFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkGikK'
|
||||||
'DVJlZGVlbVZvdWNoZXISGAoHdm91Y2hlchgBIAEoCVIHdm91Y2hlchpNChFTd2l0Y2hUb1BheW'
|
'DVJlZGVlbVZvdWNoZXISGAoHdm91Y2hlchgBIAEoCVIHdm91Y2hlchpwChFTd2l0Y2hUb1BheW'
|
||||||
'VkUGxhbhIXCgdwbGFuX2lkGAEgASgJUgZwbGFuSWQSHwoLcGF5X21vbnRobHkYAiABKAhSCnBh'
|
'VkUGxhbhIXCgdwbGFuX2lkGAEgASgJUgZwbGFuSWQSHwoLcGF5X21vbnRobHkYAiABKAhSCnBh'
|
||||||
'eU1vbnRobHkaMAoNQ3JlYXRlVm91Y2hlchIfCgt2YWx1ZV9jZW50cxgBIAEoDVIKdmFsdWVDZW'
|
'eU1vbnRobHkSIQoMYXV0b19yZW5ld2FsGAMgASgIUgthdXRvUmVuZXdhbBowCg1DcmVhdGVWb3'
|
||||||
'50cxoNCgtHZXRMb2NhdGlvbhoNCgtHZXRWb3VjaGVycxoTChFHZXRBdmFpbGFibGVQbGFucxoX'
|
'VjaGVyEh8KC3ZhbHVlX2NlbnRzGAEgASgNUgp2YWx1ZUNlbnRzGg0KC0dldExvY2F0aW9uGg0K'
|
||||||
'ChVHZXRBZGRBY2NvdW50c0ludml0ZXMaFQoTR2V0Q3VycmVudFBsYW5JbmZvcxo3ChRSZWRlZW'
|
'C0dldFZvdWNoZXJzGhMKEUdldEF2YWlsYWJsZVBsYW5zGhcKFUdldEFkZEFjY291bnRzSW52aX'
|
||||||
'1BZGRpdGlvbmFsQ29kZRIfCgtpbnZpdGVfY29kZRgCIAEoCVIKaW52aXRlQ29kZRotChJHZXRQ'
|
'RlcxoVChNHZXRDdXJyZW50UGxhbkluZm9zGjcKFFJlZGVlbUFkZGl0aW9uYWxDb2RlEh8KC2lu'
|
||||||
'cmVrZXlzQnlVc2VySWQSFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkGjsKDkdldFVwbG9hZFRva2'
|
'dml0ZV9jb2RlGAIgASgJUgppbnZpdGVDb2RlGi0KEkdldFByZWtleXNCeVVzZXJJZBIXCgd1c2'
|
||||||
'VuEikKEHJlY2lwaWVudHNfY291bnQYASABKA1SD3JlY2lwaWVudHNDb3VudBqJAQoKVXBsb2Fk'
|
'VyX2lkGAEgASgDUgZ1c2VySWQaOwoOR2V0VXBsb2FkVG9rZW4SKQoQcmVjaXBpZW50c19jb3Vu'
|
||||||
'RGF0YRIhCgx1cGxvYWRfdG9rZW4YASABKAxSC3VwbG9hZFRva2VuEhYKBm9mZnNldBgCIAEoDV'
|
'dBgBIAEoDVIPcmVjaXBpZW50c0NvdW50GokBCgpVcGxvYWREYXRhEiEKDHVwbG9hZF90b2tlbh'
|
||||||
'IGb2Zmc2V0EhIKBGRhdGEYAyABKAxSBGRhdGESHwoIY2hlY2tzdW0YBCABKAxIAFIIY2hlY2tz'
|
'gBIAEoDFILdXBsb2FkVG9rZW4SFgoGb2Zmc2V0GAIgASgNUgZvZmZzZXQSEgoEZGF0YRgDIAEo'
|
||||||
'dW2IAQFCCwoJX2NoZWNrc3VtGk0KDERvd25sb2FkRGF0YRIlCg5kb3dubG9hZF90b2tlbhgBIA'
|
'DFIEZGF0YRIfCghjaGVja3N1bRgEIAEoDEgAUghjaGVja3N1bYgBAUILCglfY2hlY2tzdW0aTQ'
|
||||||
'EoDFINZG93bmxvYWRUb2tlbhIWCgZvZmZzZXQYAiABKA1SBm9mZnNldEIRCg9BcHBsaWNhdGlv'
|
'oMRG93bmxvYWREYXRhEiUKDmRvd25sb2FkX3Rva2VuGAEgASgMUg1kb3dubG9hZFRva2VuEhYK'
|
||||||
'bkRhdGE=');
|
'Bm9mZnNldBgCIAEoDVIGb2Zmc2V0QhEKD0FwcGxpY2F0aW9uRGF0YQ==');
|
||||||
|
|
||||||
@$core.Deprecated('Use responseDescriptor instead')
|
@$core.Deprecated('Use responseDescriptor instead')
|
||||||
const Response$json = {
|
const Response$json = {
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ class ErrorCode extends $pb.ProtobufEnum {
|
||||||
static const ErrorCode PlanLimitReached = ErrorCode._(1023, _omitEnumNames ? '' : 'PlanLimitReached');
|
static const ErrorCode PlanLimitReached = ErrorCode._(1023, _omitEnumNames ? '' : 'PlanLimitReached');
|
||||||
static const ErrorCode NotEnoughCredit = ErrorCode._(1024, _omitEnumNames ? '' : 'NotEnoughCredit');
|
static const ErrorCode NotEnoughCredit = ErrorCode._(1024, _omitEnumNames ? '' : 'NotEnoughCredit');
|
||||||
static const ErrorCode PlanDowngrade = ErrorCode._(1025, _omitEnumNames ? '' : 'PlanDowngrade');
|
static const ErrorCode PlanDowngrade = ErrorCode._(1025, _omitEnumNames ? '' : 'PlanDowngrade');
|
||||||
|
static const ErrorCode PlanUpgradeNotYearly = ErrorCode._(1026, _omitEnumNames ? '' : 'PlanUpgradeNotYearly');
|
||||||
|
|
||||||
static const $core.List<ErrorCode> values = <ErrorCode> [
|
static const $core.List<ErrorCode> values = <ErrorCode> [
|
||||||
Unknown,
|
Unknown,
|
||||||
|
|
@ -72,6 +73,7 @@ class ErrorCode extends $pb.ProtobufEnum {
|
||||||
PlanLimitReached,
|
PlanLimitReached,
|
||||||
NotEnoughCredit,
|
NotEnoughCredit,
|
||||||
PlanDowngrade,
|
PlanDowngrade,
|
||||||
|
PlanUpgradeNotYearly,
|
||||||
];
|
];
|
||||||
|
|
||||||
static final $core.Map<$core.int, ErrorCode> _byValue = $pb.ProtobufEnum.initByValue(values);
|
static final $core.Map<$core.int, ErrorCode> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ const ErrorCode$json = {
|
||||||
{'1': 'PlanLimitReached', '2': 1023},
|
{'1': 'PlanLimitReached', '2': 1023},
|
||||||
{'1': 'NotEnoughCredit', '2': 1024},
|
{'1': 'NotEnoughCredit', '2': 1024},
|
||||||
{'1': 'PlanDowngrade', '2': 1025},
|
{'1': 'PlanDowngrade', '2': 1025},
|
||||||
|
{'1': 'PlanUpgradeNotYearly', '2': 1026},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -62,5 +63,5 @@ final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode(
|
||||||
'4Q+QcSGAoTQXBpRW5kcG9pbnROb3RGb3VuZBD6BxIWChFBdXRoVG9rZW5Ob3RWYWxpZBD7BxIT'
|
'4Q+QcSGAoTQXBpRW5kcG9pbnROb3RGb3VuZBD6BxIWChFBdXRoVG9rZW5Ob3RWYWxpZBD7BxIT'
|
||||||
'Cg5JbnZhbGlkUHJlS2V5cxD8BxITCg5Wb3VjaGVySW5WYWxpZBD9BxITCg5QbGFuTm90QWxsb3'
|
'Cg5JbnZhbGlkUHJlS2V5cxD8BxITCg5Wb3VjaGVySW5WYWxpZBD9BxITCg5QbGFuTm90QWxsb3'
|
||||||
'dlZBD+BxIVChBQbGFuTGltaXRSZWFjaGVkEP8HEhQKD05vdEVub3VnaENyZWRpdBCACBISCg1Q'
|
'dlZBD+BxIVChBQbGFuTGltaXRSZWFjaGVkEP8HEhQKD05vdEVub3VnaENyZWRpdBCACBISCg1Q'
|
||||||
'bGFuRG93bmdyYWRlEIEI');
|
'bGFuRG93bmdyYWRlEIEIEhkKFFBsYW5VcGdyYWRlTm90WWVhcmx5EIII');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -440,6 +440,17 @@ class ApiProvider {
|
||||||
return await sendRequestSync(req);
|
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 {
|
Future<Result> redeemVoucher(String voucher) async {
|
||||||
var get = ApplicationData_RedeemVoucher()..voucher = voucher;
|
var get = ApplicationData_RedeemVoucher()..voucher = voucher;
|
||||||
var appData = ApplicationData()..redeemvoucher = get;
|
var appData = ApplicationData()..redeemvoucher = get;
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,8 @@ String errorCodeToText(BuildContext context, ErrorCode code) {
|
||||||
return context.lang.errorPlanNotAllowed;
|
return context.lang.errorPlanNotAllowed;
|
||||||
case ErrorCode.VoucherInValid:
|
case ErrorCode.VoucherInValid:
|
||||||
return context.lang.errorVoucherInvalid;
|
return context.lang.errorVoucherInvalid;
|
||||||
|
case ErrorCode.PlanUpgradeNotYearly:
|
||||||
|
return context.lang.errorPlanUpgradeNotYearly;
|
||||||
default:
|
default:
|
||||||
return code.toString(); // Fallback for unrecognized keys
|
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/misc.dart';
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
import 'package:twonly/src/views/components/better_list_title.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/transaction_view.dart';
|
||||||
import 'package:twonly/src/views/settings/subscription/voucher_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 {
|
class SubscriptionView extends StatefulWidget {
|
||||||
const SubscriptionView({super.key});
|
const SubscriptionView({super.key});
|
||||||
|
|
||||||
|
|
@ -31,21 +69,7 @@ class _SubscriptionViewState extends State<SubscriptionView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future initAsync() async {
|
Future initAsync() async {
|
||||||
final user = await getUser();
|
ballance = await loadPlanBallance();
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,25 +134,25 @@ class _SubscriptionViewState extends State<SubscriptionView> {
|
||||||
),
|
),
|
||||||
if (currentPlan != "Family" && currentPlan != "Pro")
|
if (currentPlan != "Family" && currentPlan != "Pro")
|
||||||
PlanCard(
|
PlanCard(
|
||||||
title: "Pro",
|
planId: "Pro",
|
||||||
yearlyPrice: context.lang.proYearlyPrice,
|
onTap: () async {
|
||||||
monthlyPrice: context.lang.proMonthlyPrice,
|
await Navigator.push(context,
|
||||||
features: [
|
MaterialPageRoute(builder: (context) {
|
||||||
context.lang.proFeature1,
|
return CheckoutView(planId: "Pro");
|
||||||
context.lang.proFeature2,
|
}));
|
||||||
context.lang.proFeature3,
|
initAsync();
|
||||||
],
|
},
|
||||||
),
|
),
|
||||||
if (currentPlan != "Family")
|
if (currentPlan != "Family")
|
||||||
PlanCard(
|
PlanCard(
|
||||||
title: "Family",
|
planId: "Family",
|
||||||
yearlyPrice: context.lang.familyYearlyPrice,
|
onTap: () async {
|
||||||
monthlyPrice: context.lang.familyMonthlyPrice,
|
await Navigator.push(context,
|
||||||
features: [
|
MaterialPageRoute(builder: (context) {
|
||||||
context.lang.familyFeature1,
|
return CheckoutView(planId: "Family");
|
||||||
context.lang.familyFeature2,
|
}));
|
||||||
context.lang.familyFeature3,
|
initAsync();
|
||||||
],
|
},
|
||||||
),
|
),
|
||||||
if (currentPlan == "Preview" || currentPlan == "Free") ...[
|
if (currentPlan == "Preview" || currentPlan == "Free") ...[
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
|
|
@ -145,31 +169,23 @@ class _SubscriptionViewState extends State<SubscriptionView> {
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
if (currentPlan != "Free")
|
if (currentPlan != "Free")
|
||||||
PlanCard(
|
PlanCard(
|
||||||
title: "Free",
|
planId: "Free",
|
||||||
yearlyPrice: "",
|
onTap: () {},
|
||||||
monthlyPrice: "",
|
|
||||||
features: [
|
|
||||||
context.lang.freeFeature1,
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
PlanCard(
|
PlanCard(
|
||||||
title: "Plus",
|
planId: "Plus",
|
||||||
yearlyPrice: "",
|
onTap: () {},
|
||||||
monthlyPrice: "",
|
|
||||||
features: [
|
|
||||||
context.lang.plusFeature1,
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
if (currentPlan != "Family") Divider(),
|
if (currentPlan != "Family") Divider(),
|
||||||
if (currentPlan == "Family" || currentPlan == "Pro")
|
if (currentPlan == "Family" || currentPlan == "Pro")
|
||||||
BetterListTile(
|
BetterListTile(
|
||||||
icon: FontAwesomeIcons.userPlus,
|
icon: FontAwesomeIcons.gears,
|
||||||
text: "Manage your subscription",
|
text: context.lang.manageSubscription,
|
||||||
subtitle: (nextPayment != null)
|
subtitle: (nextPayment != null)
|
||||||
? Text(
|
? Text(
|
||||||
"Next payment: ${DateFormat.yMMMMd(myLocale.toString()).format(nextPayment)}")
|
"${context.lang.nextPayment}: ${DateFormat.yMMMMd(myLocale.toString()).format(nextPayment)}")
|
||||||
: null,
|
: null,
|
||||||
onTap: () {},
|
onTap: () {},
|
||||||
),
|
),
|
||||||
|
|
@ -214,24 +230,60 @@ 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 {
|
class PlanCard extends StatelessWidget {
|
||||||
final String title;
|
final String planId;
|
||||||
final String yearlyPrice;
|
final Function()? onTap;
|
||||||
final String monthlyPrice;
|
|
||||||
final List<String> features;
|
|
||||||
|
|
||||||
const PlanCard({
|
const PlanCard({
|
||||||
super.key,
|
super.key,
|
||||||
required this.title,
|
required this.planId,
|
||||||
required this.yearlyPrice,
|
this.onTap,
|
||||||
required this.monthlyPrice,
|
|
||||||
required this.features,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
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(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(left: 16, right: 16),
|
padding: const EdgeInsets.only(left: 16, right: 16),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
child: Card(
|
child: Card(
|
||||||
elevation: 4,
|
elevation: 4,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
|
|
@ -247,19 +299,19 @@ class PlanCard extends StatelessWidget {
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
title,
|
planId,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (yearlyPrice != "") SizedBox(height: 10),
|
if (yearlyPrice != 0) SizedBox(height: 10),
|
||||||
if (yearlyPrice != "")
|
if (yearlyPrice != 0)
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
yearlyPrice,
|
"${localePrizing(context, yearlyPrice)}/${context.lang.year}",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
|
|
@ -267,7 +319,7 @@ class PlanCard extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
monthlyPrice,
|
"${localePrizing(context, monthlyPrice)}/${context.lang.month}",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
|
|
@ -292,6 +344,7 @@ class PlanCard extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,6 @@ class TransactionView extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TransactionViewState extends State<TransactionView> {
|
class _TransactionViewState extends State<TransactionView> {
|
||||||
// String typeToText(String type) {
|
|
||||||
|
|
||||||
// switch (type) {
|
|
||||||
// case "VoucherCreated":
|
|
||||||
|
|
||||||
// break;
|
|
||||||
// default:
|
|
||||||
// }
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue