From 12951c3c48a66500a91cbbfcd67814261d9daac6 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Tue, 4 Mar 2025 11:17:28 -0500 Subject: [PATCH] refactor: add necessary info to subscription options --- assets/l10n/intl_en.arb | 23 +- .../controllers/subscription_controller.dart | 4 +- .../pages/change_subscription.dart | 215 ++++++++++++++---- .../pages/settings_subscription.dart | 11 +- .../widgets/subscription_buttons.dart | 57 ----- 5 files changed, 211 insertions(+), 99 deletions(-) delete mode 100644 lib/pangea/subscription/widgets/subscription_buttons.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 47860de0c..87a6e18e8 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4830,5 +4830,26 @@ "noSpaceDescriptionYet": "No space description created yet.", "tooLargeToSend": "This message is too large to send", "leaveRoomDescription": "You're about to leave this chat. Other users will see that you have left the chat.", - "confirmUserId": "Please confirm your Pangea Chat username in order to delete your account." + "confirmUserId": "Please confirm your Pangea Chat username in order to delete your account.", + "startingToday": "Starting today", + "oneWeekFreeTrial": "One week free trial", + "paidSubscriptionStarts": "Starting {startDate}", + "@paidSubscriptionStarts": { + "type": "String", + "placeholders": { + "startDate": { + "type": "String" + } + } + }, + "cancelInSubscriptionSettings": "• Cancel at any time in subscription settings", + "cancelToAvoidCharges": "• Cancel before {trialEnds} to avoid charges", + "@cancelToAvoidCharges": { + "type": "String", + "placeholders": { + "trialEnds": { + "type": "String" + } + } + } } \ No newline at end of file diff --git a/lib/pangea/subscription/controllers/subscription_controller.dart b/lib/pangea/subscription/controllers/subscription_controller.dart index 1dc46d150..4c2db1d92 100644 --- a/lib/pangea/subscription/controllers/subscription_controller.dart +++ b/lib/pangea/subscription/controllers/subscription_controller.dart @@ -147,7 +147,7 @@ class SubscriptionController extends BaseController { } } - void submitSubscriptionChange( + Future submitSubscriptionChange( SubscriptionDetails? selectedSubscription, BuildContext context, { bool isPromo = false, @@ -185,11 +185,13 @@ class SubscriptionController extends BaseController { return; } if (selectedSubscription.package == null) { + final offerings = await Purchases.getOfferings(); ErrorHandler.logError( m: "Tried to subscribe to SubscriptionDetails with Null revenuecat Package", s: StackTrace.current, data: { "selectedSubscription": selectedSubscription.toJson(), + "offerings": offerings.toJson(), }, ); return; diff --git a/lib/pangea/subscription/pages/change_subscription.dart b/lib/pangea/subscription/pages/change_subscription.dart index 5522b7d78..7b7bc7424 100644 --- a/lib/pangea/subscription/pages/change_subscription.dart +++ b/lib/pangea/subscription/pages/change_subscription.dart @@ -1,10 +1,13 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:intl/intl.dart'; +import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; +import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart'; import 'package:fluffychat/pangea/subscription/pages/settings_subscription.dart'; -import 'package:fluffychat/pangea/subscription/widgets/subscription_buttons.dart'; import 'package:fluffychat/widgets/matrix.dart'; class ChangeSubscription extends StatelessWidget { @@ -16,51 +19,187 @@ class ChangeSubscription extends StatelessWidget { final PangeaController pangeaController = MatrixState.pangeaController; + List get subscriptions => + pangeaController.subscriptionController.availableSubscriptionInfo + ?.availableSubscriptions ?? + []; + + bool get inTrialWindow => pangeaController.userController.inTrialWindow(); + + String get trialEnds => DateFormat.yMMMd() + .format(DateTime.now().add(const Duration(days: kIsWeb ? 0 : 7))); + @override Widget build(BuildContext context) { - return controller.subscriptionsAvailable - ? Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - L10n.of(context).selectYourPlan, - style: const TextStyle(fontSize: 16), - ), - const SizedBox(height: 16.0), - const Divider(height: 1), - SubscriptionButtons(controller: controller), - const SizedBox(height: 32), - IntrinsicWidth( + if (!controller.subscriptionsAvailable) { + return const Center( + child: Padding( + padding: EdgeInsets.all(16.0), + child: CircularProgressIndicator.adaptive( + strokeWidth: 2, + ), + ), + ); + } + + return Column( + spacing: 16.0, + children: [ + Text( + L10n.of(context).selectYourPlan, + style: const TextStyle(fontSize: 16), + ), + Column( + children: [ + for (final subscription in subscriptions) + DecoratedBox( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Theme.of(context).dividerColor, + width: 1.0, + ), + ), + ), child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - OutlinedButton( - onPressed: controller.selectedSubscription != null - ? () => controller.submitChange() - : null, - child: controller.loading - ? const CircularProgressIndicator.adaptive() - : Text( - controller.selectedSubscription?.isTrial ?? false - ? L10n.of(context).activateTrial - : L10n.of(context).pay, + ListTile( + title: Text( + subscription.displayName(context), + ), + trailing: Icon( + controller.selectedSubscription?.id != subscription.id + ? Icons.keyboard_arrow_right_outlined + : Icons.keyboard_arrow_down_outlined, + ), + enabled: (!subscription.isTrial || inTrialWindow) && + !controller.isCurrentSubscription(subscription), + onTap: () => controller.selectSubscription(subscription), + ), + AnimatedSize( + duration: FluffyThemes.animationDuration, + child: controller.selectedSubscription?.id != + subscription.id + ? const SizedBox() + : Column( + children: [ + Container( + constraints: const BoxConstraints( + maxWidth: 400.0, + ), + decoration: BoxDecoration( + border: Border.all( + color: Theme.of(context).dividerColor, + ), + borderRadius: const BorderRadius.all( + Radius.circular(16.0), + ), + ), + margin: const EdgeInsets.all(8.0), + child: Column( + children: [ + if (!kIsWeb) + Container( + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .onPrimary, + borderRadius: + const BorderRadius.only( + topLeft: Radius.circular(16.0), + topRight: Radius.circular(16.0), + ), + ), + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + L10n.of(context).startingToday, + ), + Text( + L10n.of(context) + .oneWeekFreeTrial, + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + L10n.of(context) + .paidSubscriptionStarts( + trialEnds, + ), + ), + Text( + "${subscription.displayPrice(context)}/${subscription.duration?.value}", + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ], + ), + ), + Container( + constraints: const BoxConstraints( + maxWidth: 400.0, + ), + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + L10n.of(context) + .cancelInSubscriptionSettings, + ), + if (!kIsWeb) + Text( + L10n.of(context) + .cancelToAvoidCharges(trialEnds), + ), + const SizedBox(height: 20.0), + ElevatedButton( + onPressed: () => controller + .submitChange(subscription), + child: Row( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + controller.loading + ? const CircularProgressIndicator + .adaptive() + : Text( + subscription.isTrial + ? L10n.of(context) + .activateTrial + : L10n.of(context).pay, + ), + ], + ), + ), + ], + ), + ), + ], ), ), - const SizedBox(height: 20), ], ), ), - ], - ) - : const Center( - child: Padding( - padding: EdgeInsets.all(16.0), - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, - ), - ), - ); + ], + ), + const SizedBox(height: 20.0), + ], + ); } } diff --git a/lib/pangea/subscription/pages/settings_subscription.dart b/lib/pangea/subscription/pages/settings_subscription.dart index f2edaee14..6ce327a04 100644 --- a/lib/pangea/subscription/pages/settings_subscription.dart +++ b/lib/pangea/subscription/pages/settings_subscription.dart @@ -102,12 +102,15 @@ class SubscriptionManagementController extends State { .currentSubscriptionInfo!.currentPlatformMatchesPurchasePlatform; } - Future submitChange({bool isPromo = false}) async { + Future submitChange( + SubscriptionDetails subscription, { + bool isPromo = false, + }) async { setState(() => loading = true); await showFutureLoadingDialog( context: context, future: () async => subscriptionController.submitSubscriptionChange( - selectedSubscription, + subscription, context, isPromo: isPromo, ), @@ -170,6 +173,10 @@ class SubscriptionManagementController extends State { } void selectSubscription(SubscriptionDetails? subscription) { + if (selectedSubscription == subscription) { + setState(() => selectedSubscription = null); + return; + } setState(() => selectedSubscription = subscription); } diff --git a/lib/pangea/subscription/widgets/subscription_buttons.dart b/lib/pangea/subscription/widgets/subscription_buttons.dart deleted file mode 100644 index ff59c467c..000000000 --- a/lib/pangea/subscription/widgets/subscription_buttons.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:flutter_gen/gen_l10n/l10n.dart'; - -import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; -import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart'; -import 'package:fluffychat/pangea/subscription/pages/settings_subscription.dart'; -import 'package:fluffychat/widgets/matrix.dart'; - -class SubscriptionButtons extends StatelessWidget { - final SubscriptionManagementController controller; - final PangeaController pangeaController = MatrixState.pangeaController; - SubscriptionButtons({ - required this.controller, - super.key, - }); - - @override - Widget build(BuildContext context) { - final bool inTrialWindow = pangeaController.userController.inTrialWindow(); - return ListView.builder( - shrinkWrap: true, - itemCount: controller.subscriptionController.availableSubscriptionInfo! - .availableSubscriptions.length, - itemBuilder: (BuildContext context, int i) { - final SubscriptionDetails subscription = pangeaController - .subscriptionController - .availableSubscriptionInfo! - .availableSubscriptions[i]; - return Column( - children: [ - ListTile( - title: Text( - subscription.displayName(context), - ), - subtitle: Text( - subscription.isTrial && !inTrialWindow - ? L10n.of(context).trialPeriodExpired - : subscription.displayPrice(context), - ), - trailing: const Icon(Icons.keyboard_arrow_right_outlined), - selected: controller.selectedSubscription == subscription, - selectedTileColor: - Theme.of(context).colorScheme.secondary.withAlpha(16), - enabled: (!subscription.isTrial || inTrialWindow) && - !controller.isCurrentSubscription(subscription), - onTap: () { - controller.selectSubscription(subscription); - }, - ), - const Divider(height: 1), - ], - ); - }, - ); - } -}