2021-08-20 12:06:05 +00:00
|
|
|
import 'dart:async';
|
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
2022-06-07 04:40:29 +00:00
|
|
|
import 'package:photos/ente_theme_data.dart';
|
2021-08-20 12:06:05 +00:00
|
|
|
import 'package:photos/models/billing_plan.dart';
|
|
|
|
import 'package:photos/models/subscription.dart';
|
2022-04-20 20:14:44 +00:00
|
|
|
import 'package:photos/models/user_details.dart';
|
2021-08-20 12:06:05 +00:00
|
|
|
import 'package:photos/services/billing_service.dart';
|
2022-04-14 14:17:41 +00:00
|
|
|
import 'package:photos/services/user_service.dart';
|
2022-12-19 14:04:25 +00:00
|
|
|
import 'package:photos/theme/ente_theme.dart';
|
2022-07-03 10:09:01 +00:00
|
|
|
import 'package:photos/ui/common/bottom_shadow.dart';
|
2021-08-20 12:06:05 +00:00
|
|
|
import 'package:photos/ui/common/dialogs.dart';
|
2022-07-01 14:18:05 +00:00
|
|
|
import 'package:photos/ui/common/loading_widget.dart';
|
|
|
|
import 'package:photos/ui/common/progress_dialog.dart';
|
|
|
|
import 'package:photos/ui/common/web_page.dart';
|
2022-04-20 20:14:44 +00:00
|
|
|
import 'package:photos/ui/payment/child_subscription_widget.dart';
|
2021-08-20 12:06:05 +00:00
|
|
|
import 'package:photos/ui/payment/payment_web_page.dart';
|
|
|
|
import 'package:photos/ui/payment/skip_subscription_widget.dart';
|
2021-08-20 20:31:12 +00:00
|
|
|
import 'package:photos/ui/payment/subscription_common_widgets.dart';
|
2021-08-20 12:06:05 +00:00
|
|
|
import 'package:photos/ui/payment/subscription_plan_widget.dart';
|
|
|
|
import 'package:photos/utils/dialog_util.dart';
|
|
|
|
import 'package:photos/utils/toast_util.dart';
|
2022-06-02 11:06:24 +00:00
|
|
|
import 'package:step_progress_indicator/step_progress_indicator.dart';
|
2022-07-11 03:51:08 +00:00
|
|
|
import 'package:url_launcher/url_launcher_string.dart';
|
2021-08-20 12:06:05 +00:00
|
|
|
|
|
|
|
class StripeSubscriptionPage extends StatefulWidget {
|
|
|
|
final bool isOnboarding;
|
|
|
|
|
|
|
|
const StripeSubscriptionPage({
|
|
|
|
this.isOnboarding = false,
|
2022-12-30 12:10:17 +00:00
|
|
|
Key? key,
|
2021-08-20 12:06:05 +00:00
|
|
|
}) : super(key: key);
|
|
|
|
|
|
|
|
@override
|
2022-07-03 09:45:00 +00:00
|
|
|
State<StripeSubscriptionPage> createState() => _StripeSubscriptionPageState();
|
2021-08-20 12:06:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
|
|
|
|
final _billingService = BillingService.instance;
|
2022-04-14 14:17:41 +00:00
|
|
|
final _userService = UserService.instance;
|
2022-12-30 12:10:17 +00:00
|
|
|
Subscription? _currentSubscription;
|
|
|
|
late ProgressDialog _dialog;
|
|
|
|
late UserDetails _userDetails;
|
2021-08-20 12:06:05 +00:00
|
|
|
|
|
|
|
// indicates if user's subscription plan is still active
|
2022-12-30 12:10:17 +00:00
|
|
|
late bool _hasActiveSubscription;
|
|
|
|
late FreePlan _freePlan;
|
2021-08-20 12:06:05 +00:00
|
|
|
List<BillingPlan> _plans = [];
|
|
|
|
bool _hasLoadedData = false;
|
2022-04-26 11:52:09 +00:00
|
|
|
bool _isLoading = false;
|
2021-09-27 05:30:51 +00:00
|
|
|
bool _isStripeSubscriber = false;
|
2021-08-20 12:06:05 +00:00
|
|
|
bool _showYearlyPlan = false;
|
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _fetchSub() async {
|
2022-07-03 09:49:33 +00:00
|
|
|
return _userService
|
|
|
|
.getUserDetailsV2(memoryCount: false)
|
|
|
|
.then((userDetails) async {
|
2022-04-20 20:14:44 +00:00
|
|
|
_userDetails = userDetails;
|
2022-04-14 14:17:41 +00:00
|
|
|
_currentSubscription = userDetails.subscription;
|
2022-12-30 12:10:17 +00:00
|
|
|
_showYearlyPlan = _currentSubscription!.isYearlyPlan();
|
|
|
|
_hasActiveSubscription = _currentSubscription!.isValid();
|
|
|
|
_isStripeSubscriber = _currentSubscription!.paymentProvider == stripe;
|
2021-08-21 08:47:34 +00:00
|
|
|
return _filterStripeForUI().then((value) {
|
2021-08-20 12:06:05 +00:00
|
|
|
_hasLoadedData = true;
|
|
|
|
setState(() {});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// _filterPlansForUI is used for initializing initState & plan toggle states
|
2021-08-21 08:47:34 +00:00
|
|
|
Future<void> _filterStripeForUI() async {
|
2021-08-20 12:06:05 +00:00
|
|
|
final billingPlans = await _billingService.getBillingPlans();
|
|
|
|
_freePlan = billingPlans.freePlan;
|
|
|
|
_plans = billingPlans.plans.where((plan) {
|
2022-12-30 15:42:03 +00:00
|
|
|
if (plan.stripeID.isEmpty) {
|
2021-08-21 08:47:34 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-08-23 10:15:45 +00:00
|
|
|
final isYearlyPlan = plan.period == 'year';
|
2021-08-21 08:47:34 +00:00
|
|
|
return isYearlyPlan == _showYearlyPlan;
|
2021-08-20 12:06:05 +00:00
|
|
|
}).toList();
|
|
|
|
setState(() {});
|
|
|
|
}
|
|
|
|
|
|
|
|
FutureOr onWebPaymentGoBack(dynamic value) async {
|
2021-09-27 04:42:38 +00:00
|
|
|
// refresh subscription
|
|
|
|
await _dialog.show();
|
|
|
|
try {
|
|
|
|
await _fetchSub();
|
|
|
|
} catch (e) {
|
2022-06-10 14:29:56 +00:00
|
|
|
showToast(context, "Failed to refresh subscription");
|
2021-09-27 04:42:38 +00:00
|
|
|
}
|
|
|
|
await _dialog.hide();
|
|
|
|
|
|
|
|
// verify user has subscribed before redirecting to main page
|
|
|
|
if (widget.isOnboarding &&
|
|
|
|
_currentSubscription != null &&
|
2022-12-30 12:10:17 +00:00
|
|
|
_currentSubscription!.isValid() &&
|
|
|
|
_currentSubscription!.productID != freeProductID) {
|
2021-08-20 12:06:05 +00:00
|
|
|
Navigator.of(context).popUntil((route) => route.isFirst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2022-06-03 07:21:53 +00:00
|
|
|
final appBar = PreferredSize(
|
2022-07-04 06:02:17 +00:00
|
|
|
preferredSize: const Size(double.infinity, 60),
|
2022-06-03 07:21:53 +00:00
|
|
|
child: Container(
|
2022-06-11 08:23:52 +00:00
|
|
|
decoration: BoxDecoration(
|
|
|
|
boxShadow: [
|
2022-06-03 07:21:53 +00:00
|
|
|
BoxShadow(
|
2022-06-11 08:23:52 +00:00
|
|
|
color: Theme.of(context).backgroundColor,
|
|
|
|
blurRadius: 16,
|
2022-07-04 06:02:17 +00:00
|
|
|
offset: const Offset(0, 8),
|
2022-06-11 08:23:52 +00:00
|
|
|
)
|
|
|
|
],
|
|
|
|
),
|
|
|
|
child: widget.isOnboarding
|
|
|
|
? AppBar(
|
|
|
|
elevation: 0,
|
|
|
|
title: Hero(
|
|
|
|
tag: "subscription",
|
|
|
|
child: StepProgressIndicator(
|
|
|
|
totalSteps: 4,
|
|
|
|
currentStep: 4,
|
2022-07-12 06:30:02 +00:00
|
|
|
selectedColor:
|
|
|
|
Theme.of(context).colorScheme.greenAlternative,
|
2022-07-04 06:02:17 +00:00
|
|
|
roundedEdges: const Radius.circular(10),
|
2022-07-03 09:49:33 +00:00
|
|
|
unselectedColor: Theme.of(context)
|
|
|
|
.colorScheme
|
|
|
|
.stepProgressUnselectedColor,
|
2022-06-11 08:23:52 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
: AppBar(
|
|
|
|
elevation: 0,
|
2022-07-04 06:02:17 +00:00
|
|
|
title: const Text("Subscription"),
|
2022-06-11 08:23:52 +00:00
|
|
|
),
|
|
|
|
),
|
2022-06-03 07:21:53 +00:00
|
|
|
);
|
2021-08-20 12:06:05 +00:00
|
|
|
return Scaffold(
|
|
|
|
appBar: appBar,
|
2022-06-03 07:21:53 +00:00
|
|
|
body: Stack(
|
|
|
|
alignment: Alignment.bottomCenter,
|
|
|
|
children: [
|
|
|
|
_getBody(),
|
2022-07-04 06:02:17 +00:00
|
|
|
const BottomShadowWidget(
|
2022-06-03 07:21:53 +00:00
|
|
|
offsetDy: 40,
|
|
|
|
)
|
|
|
|
],
|
|
|
|
),
|
2021-08-20 12:06:05 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _getBody() {
|
2022-04-26 11:52:09 +00:00
|
|
|
if (!_isLoading) {
|
|
|
|
_isLoading = true;
|
2022-05-17 11:38:21 +00:00
|
|
|
_dialog = createProgressDialog(context, "Please wait...");
|
2022-04-26 11:52:09 +00:00
|
|
|
_fetchSub();
|
|
|
|
}
|
2021-08-20 12:06:05 +00:00
|
|
|
if (_hasLoadedData) {
|
2022-04-20 20:14:44 +00:00
|
|
|
if (_userDetails.isPartOfFamily() && !_userDetails.isFamilyAdmin()) {
|
|
|
|
return ChildSubscriptionWidget(userDetails: _userDetails);
|
2022-04-20 21:12:34 +00:00
|
|
|
} else {
|
|
|
|
return _buildPlans();
|
2022-04-20 20:14:44 +00:00
|
|
|
}
|
2021-08-20 12:06:05 +00:00
|
|
|
}
|
2022-07-03 06:04:42 +00:00
|
|
|
return const EnteLoadingWidget();
|
2021-08-20 12:06:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Widget _buildPlans() {
|
|
|
|
final widgets = <Widget>[];
|
2021-08-20 20:31:12 +00:00
|
|
|
|
2022-06-11 08:23:52 +00:00
|
|
|
widgets.add(
|
|
|
|
SubscriptionHeaderWidget(
|
|
|
|
isOnboarding: widget.isOnboarding,
|
|
|
|
currentUsage: _userDetails.getFamilyOrPersonalUsage(),
|
|
|
|
),
|
|
|
|
);
|
2021-08-20 20:31:12 +00:00
|
|
|
|
2021-08-20 12:06:05 +00:00
|
|
|
widgets.addAll([
|
|
|
|
Column(
|
2022-06-11 08:23:52 +00:00
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
children: _getStripePlanWidgets(),
|
|
|
|
),
|
2022-07-04 06:02:17 +00:00
|
|
|
const Padding(padding: EdgeInsets.all(4)),
|
2021-08-20 12:06:05 +00:00
|
|
|
]);
|
|
|
|
|
|
|
|
widgets.add(_showSubscriptionToggle());
|
|
|
|
|
|
|
|
if (_hasActiveSubscription) {
|
2021-08-20 20:31:12 +00:00
|
|
|
widgets.add(ValidityWidget(currentSubscription: _currentSubscription));
|
2021-08-20 12:06:05 +00:00
|
|
|
}
|
|
|
|
|
2022-12-30 12:10:17 +00:00
|
|
|
if (_currentSubscription!.productID == freeProductID) {
|
2021-08-20 20:59:53 +00:00
|
|
|
if (widget.isOnboarding) {
|
|
|
|
widgets.add(SkipSubscriptionWidget(freePlan: _freePlan));
|
|
|
|
}
|
2022-07-04 06:02:17 +00:00
|
|
|
widgets.add(const SubFaqWidget());
|
2021-08-20 20:59:53 +00:00
|
|
|
}
|
|
|
|
|
2021-09-27 05:47:40 +00:00
|
|
|
// only active subscription can be renewed/canceled
|
|
|
|
if (_hasActiveSubscription && _isStripeSubscriber) {
|
2021-08-20 20:31:12 +00:00
|
|
|
widgets.add(_stripeRenewOrCancelButton());
|
2021-08-20 12:06:05 +00:00
|
|
|
}
|
|
|
|
|
2022-12-30 12:10:17 +00:00
|
|
|
if (_currentSubscription!.productID != freeProductID) {
|
2021-08-20 12:06:05 +00:00
|
|
|
widgets.addAll([
|
|
|
|
Align(
|
|
|
|
alignment: Alignment.center,
|
|
|
|
child: GestureDetector(
|
|
|
|
onTap: () async {
|
2022-09-21 11:39:03 +00:00
|
|
|
final String paymentProvider =
|
2022-12-30 12:10:17 +00:00
|
|
|
_currentSubscription!.paymentProvider;
|
|
|
|
switch (_currentSubscription!.paymentProvider) {
|
2022-09-19 05:11:02 +00:00
|
|
|
case stripe:
|
2021-08-20 12:06:05 +00:00
|
|
|
await _launchStripePortal();
|
2021-08-21 14:18:04 +00:00
|
|
|
break;
|
2022-09-19 05:11:02 +00:00
|
|
|
case playStore:
|
2022-07-11 03:51:08 +00:00
|
|
|
launchUrlString(
|
2022-06-11 08:23:52 +00:00
|
|
|
"https://play.google.com/store/account/subscriptions?sku=" +
|
2022-12-30 12:10:17 +00:00
|
|
|
_currentSubscription!.productID +
|
2022-06-11 08:23:52 +00:00
|
|
|
"&package=io.ente.photos",
|
|
|
|
);
|
2021-08-21 14:18:04 +00:00
|
|
|
break;
|
2022-09-19 05:11:02 +00:00
|
|
|
case appStore:
|
2022-07-11 03:51:08 +00:00
|
|
|
launchUrlString("https://apps.apple.com/account/billing");
|
2021-08-21 14:18:04 +00:00
|
|
|
break;
|
|
|
|
default:
|
2022-09-21 11:39:03 +00:00
|
|
|
final String capitalizedWord = paymentProvider.isNotEmpty
|
|
|
|
? '${paymentProvider[0].toUpperCase()}${paymentProvider.substring(1).toLowerCase()}'
|
|
|
|
: '';
|
|
|
|
showErrorDialog(
|
|
|
|
context,
|
|
|
|
"Sorry",
|
|
|
|
"Please contact us at support@ente.io to manage your "
|
|
|
|
"$capitalizedWord subscription.",
|
2022-06-11 08:23:52 +00:00
|
|
|
);
|
2021-08-20 12:06:05 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
child: Container(
|
2022-07-04 06:02:17 +00:00
|
|
|
padding: const EdgeInsets.fromLTRB(40, 80, 40, 20),
|
2021-08-20 12:06:05 +00:00
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
RichText(
|
|
|
|
text: TextSpan(
|
2022-09-21 11:39:03 +00:00
|
|
|
text: "Payment details",
|
2022-06-11 08:23:52 +00:00
|
|
|
style: TextStyle(
|
|
|
|
color: Theme.of(context).colorScheme.onSurface,
|
|
|
|
fontFamily: 'Inter-Medium',
|
|
|
|
fontSize: 14,
|
2022-09-21 11:39:03 +00:00
|
|
|
decoration: TextDecoration.underline,
|
2022-06-11 08:23:52 +00:00
|
|
|
),
|
|
|
|
),
|
2021-08-20 12:06:05 +00:00
|
|
|
textAlign: TextAlign.center,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
]);
|
2022-05-12 04:29:04 +00:00
|
|
|
}
|
2022-04-16 16:40:18 +00:00
|
|
|
|
2022-05-12 04:29:04 +00:00
|
|
|
if (!widget.isOnboarding) {
|
|
|
|
widgets.addAll([
|
|
|
|
Align(
|
|
|
|
alignment: Alignment.topCenter,
|
|
|
|
child: GestureDetector(
|
|
|
|
onTap: () async {
|
2022-12-06 16:58:17 +00:00
|
|
|
_billingService.launchFamilyPortal(context, _userDetails);
|
2022-05-12 04:29:04 +00:00
|
|
|
},
|
|
|
|
child: Container(
|
2022-07-04 06:02:17 +00:00
|
|
|
padding: const EdgeInsets.fromLTRB(40, 0, 40, 80),
|
2022-05-12 04:29:04 +00:00
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
RichText(
|
|
|
|
text: TextSpan(
|
2022-06-09 08:29:30 +00:00
|
|
|
text: "Manage family",
|
2022-12-30 12:10:17 +00:00
|
|
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
2022-10-10 17:25:24 +00:00
|
|
|
decoration: TextDecoration.underline,
|
|
|
|
),
|
2022-04-16 16:40:18 +00:00
|
|
|
),
|
2022-05-12 04:29:04 +00:00
|
|
|
textAlign: TextAlign.center,
|
|
|
|
),
|
|
|
|
],
|
2022-04-16 16:40:18 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
2022-05-12 04:29:04 +00:00
|
|
|
),
|
|
|
|
]);
|
2021-08-20 12:06:05 +00:00
|
|
|
}
|
2021-08-20 20:59:53 +00:00
|
|
|
|
2021-08-20 12:06:05 +00:00
|
|
|
return SingleChildScrollView(
|
|
|
|
child: Column(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
|
|
children: widgets,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _launchStripePortal() async {
|
|
|
|
await _dialog.show();
|
|
|
|
try {
|
2022-08-29 14:43:31 +00:00
|
|
|
final String url = await _billingService.getStripeCustomerPortalUrl();
|
2021-08-20 12:06:05 +00:00
|
|
|
Navigator.of(context).push(
|
|
|
|
MaterialPageRoute(
|
|
|
|
builder: (BuildContext context) {
|
2022-04-28 05:24:26 +00:00
|
|
|
return WebPage("Payment details", url);
|
2021-08-20 12:06:05 +00:00
|
|
|
},
|
|
|
|
),
|
|
|
|
).then((value) => onWebPaymentGoBack);
|
|
|
|
} catch (e) {
|
|
|
|
await _dialog.hide();
|
2022-12-26 11:09:29 +00:00
|
|
|
showGenericErrorDialog(context: context);
|
2021-08-20 12:06:05 +00:00
|
|
|
}
|
|
|
|
await _dialog.hide();
|
|
|
|
}
|
|
|
|
|
2021-08-20 20:31:12 +00:00
|
|
|
Widget _stripeRenewOrCancelButton() {
|
2022-08-29 14:43:31 +00:00
|
|
|
final bool isRenewCancelled =
|
2022-12-30 12:10:17 +00:00
|
|
|
_currentSubscription!.attributes?.isCancelled ?? false;
|
2022-08-29 14:43:31 +00:00
|
|
|
final String title =
|
2022-07-03 09:49:33 +00:00
|
|
|
isRenewCancelled ? "Renew subscription" : "Cancel subscription";
|
2021-08-20 12:06:05 +00:00
|
|
|
return TextButton(
|
|
|
|
child: Text(
|
2021-08-21 14:18:04 +00:00
|
|
|
title,
|
2021-08-20 12:06:05 +00:00
|
|
|
style: TextStyle(
|
2022-07-03 09:49:33 +00:00
|
|
|
color: (isRenewCancelled
|
|
|
|
? Colors.greenAccent
|
|
|
|
: Theme.of(context).colorScheme.onSurface)
|
2022-04-28 05:24:26 +00:00
|
|
|
.withOpacity(isRenewCancelled ? 1.0 : 0.2),
|
2021-08-20 12:06:05 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
onPressed: () async {
|
2021-08-27 13:40:43 +00:00
|
|
|
bool confirmAction = false;
|
|
|
|
if (isRenewCancelled) {
|
2022-08-29 14:43:31 +00:00
|
|
|
final choice = await showChoiceDialog(
|
2022-06-11 08:23:52 +00:00
|
|
|
context,
|
|
|
|
title,
|
2022-06-18 12:23:51 +00:00
|
|
|
"Are you sure you want to renew?",
|
|
|
|
firstAction: "No",
|
|
|
|
secondAction: "Yes",
|
2022-06-11 08:23:52 +00:00
|
|
|
);
|
2021-08-27 13:40:43 +00:00
|
|
|
confirmAction = choice == DialogUserChoice.secondChoice;
|
|
|
|
} else {
|
2022-08-29 14:43:31 +00:00
|
|
|
final choice = await showChoiceDialog(
|
2022-06-11 08:23:52 +00:00
|
|
|
context,
|
|
|
|
title,
|
2022-06-18 12:23:51 +00:00
|
|
|
'Are you sure you want to cancel?',
|
|
|
|
firstAction: 'Yes, cancel',
|
|
|
|
secondAction: 'No',
|
2022-06-11 08:23:52 +00:00
|
|
|
actionType: ActionType.critical,
|
|
|
|
);
|
2021-08-27 13:40:43 +00:00
|
|
|
confirmAction = choice == DialogUserChoice.firstChoice;
|
|
|
|
}
|
|
|
|
if (confirmAction) {
|
2021-08-20 20:31:12 +00:00
|
|
|
toggleStripeSubscription(isRenewCancelled);
|
2021-08-20 12:06:05 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-08-20 20:31:12 +00:00
|
|
|
Future<void> toggleStripeSubscription(bool isRenewCancelled) async {
|
2021-08-20 12:06:05 +00:00
|
|
|
await _dialog.show();
|
|
|
|
try {
|
2021-08-21 08:47:34 +00:00
|
|
|
isRenewCancelled
|
|
|
|
? await _billingService.activateStripeSubscription()
|
|
|
|
: await _billingService.cancelStripeSubscription();
|
2021-08-20 12:06:05 +00:00
|
|
|
await _fetchSub();
|
|
|
|
} catch (e) {
|
2022-12-26 05:14:36 +00:00
|
|
|
showShortToast(
|
2022-06-11 08:23:52 +00:00
|
|
|
context,
|
|
|
|
isRenewCancelled ? 'failed to renew' : 'failed to cancel',
|
|
|
|
);
|
2021-08-20 12:06:05 +00:00
|
|
|
}
|
|
|
|
await _dialog.hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
List<Widget> _getStripePlanWidgets() {
|
|
|
|
final List<Widget> planWidgets = [];
|
|
|
|
bool foundActivePlan = false;
|
|
|
|
for (final plan in _plans) {
|
|
|
|
final productID = plan.stripeID;
|
2022-12-30 15:42:03 +00:00
|
|
|
if (productID.isEmpty) {
|
2021-08-20 12:06:05 +00:00
|
|
|
continue;
|
|
|
|
}
|
2022-12-30 15:42:03 +00:00
|
|
|
final isActive = _hasActiveSubscription &&
|
|
|
|
_currentSubscription!.productID == productID;
|
2021-08-20 12:06:05 +00:00
|
|
|
if (isActive) {
|
|
|
|
foundActivePlan = true;
|
|
|
|
}
|
|
|
|
planWidgets.add(
|
|
|
|
Material(
|
|
|
|
child: InkWell(
|
|
|
|
onTap: () async {
|
|
|
|
if (isActive) {
|
|
|
|
return;
|
|
|
|
}
|
2021-09-27 05:47:40 +00:00
|
|
|
// prompt user to cancel their active subscription form other
|
|
|
|
// payment providers
|
|
|
|
if (!_isStripeSubscriber &&
|
2021-09-27 05:19:04 +00:00
|
|
|
_hasActiveSubscription &&
|
2022-12-30 12:10:17 +00:00
|
|
|
_currentSubscription!.productID != freeProductID) {
|
2022-06-11 08:23:52 +00:00
|
|
|
showErrorDialog(
|
|
|
|
context,
|
|
|
|
"Sorry",
|
2022-09-21 11:39:03 +00:00
|
|
|
"Please cancel your existing subscription from "
|
2022-12-30 12:10:17 +00:00
|
|
|
"${_currentSubscription!.paymentProvider} first",
|
2022-06-11 08:23:52 +00:00
|
|
|
);
|
2021-08-20 12:06:05 +00:00
|
|
|
return;
|
|
|
|
}
|
2022-04-20 20:36:35 +00:00
|
|
|
if (_userDetails.getFamilyOrPersonalUsage() > plan.storage) {
|
2022-04-14 14:17:41 +00:00
|
|
|
showErrorDialog(
|
2022-06-11 08:23:52 +00:00
|
|
|
context,
|
|
|
|
"Sorry",
|
2022-09-21 11:39:03 +00:00
|
|
|
"You cannot downgrade to this plan",
|
2022-06-11 08:23:52 +00:00
|
|
|
);
|
2022-04-14 14:17:41 +00:00
|
|
|
return;
|
2021-08-20 12:06:05 +00:00
|
|
|
}
|
|
|
|
String stripPurChaseAction = 'buy';
|
2021-09-27 05:30:51 +00:00
|
|
|
if (_isStripeSubscriber && _hasActiveSubscription) {
|
2021-08-20 12:06:05 +00:00
|
|
|
// confirm if user wants to change plan or not
|
2022-08-29 14:43:31 +00:00
|
|
|
final result = await showChoiceDialog(
|
2022-06-11 08:23:52 +00:00
|
|
|
context,
|
|
|
|
"Confirm plan change",
|
|
|
|
"Are you sure you want to change your plan?",
|
|
|
|
firstAction: "No",
|
|
|
|
secondAction: 'Yes',
|
|
|
|
);
|
2021-08-27 13:40:43 +00:00
|
|
|
if (result != DialogUserChoice.secondChoice) {
|
2021-08-20 12:06:05 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
stripPurChaseAction = 'update';
|
|
|
|
}
|
|
|
|
Navigator.push(
|
|
|
|
context,
|
|
|
|
MaterialPageRoute(
|
|
|
|
builder: (BuildContext context) {
|
|
|
|
return PaymentWebPage(
|
|
|
|
planId: plan.stripeID,
|
|
|
|
actionType: stripPurChaseAction,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
),
|
|
|
|
).then((value) => onWebPaymentGoBack(value));
|
|
|
|
},
|
|
|
|
child: SubscriptionPlanWidget(
|
|
|
|
storage: plan.storage,
|
|
|
|
price: plan.price,
|
|
|
|
period: plan.period,
|
|
|
|
isActive: isActive,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
2021-09-30 15:36:46 +00:00
|
|
|
if (!foundActivePlan && _hasActiveSubscription) {
|
2021-08-20 12:06:05 +00:00
|
|
|
_addCurrentPlanWidget(planWidgets);
|
|
|
|
}
|
|
|
|
return planWidgets;
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _showSubscriptionToggle() {
|
2021-08-23 10:22:07 +00:00
|
|
|
Widget _planText(String title, bool reduceOpacity) {
|
|
|
|
return Padding(
|
|
|
|
padding: const EdgeInsets.only(left: 4, right: 4),
|
|
|
|
child: Text(
|
|
|
|
title,
|
|
|
|
style: TextStyle(
|
2022-07-03 09:49:33 +00:00
|
|
|
color: Theme.of(context)
|
|
|
|
.colorScheme
|
|
|
|
.onSurface
|
|
|
|
.withOpacity(reduceOpacity ? 0.5 : 1.0),
|
2021-08-23 10:22:07 +00:00
|
|
|
),
|
2021-08-21 08:47:34 +00:00
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-08-20 12:06:05 +00:00
|
|
|
return Container(
|
2022-07-04 06:02:17 +00:00
|
|
|
padding: const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
|
|
|
|
margin: const EdgeInsets.only(bottom: 12),
|
2021-08-20 12:06:05 +00:00
|
|
|
// color: Color.fromRGBO(10, 40, 40, 0.3),
|
|
|
|
child: Row(
|
2021-08-23 10:22:07 +00:00
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
2021-08-20 12:06:05 +00:00
|
|
|
children: [
|
2022-05-30 14:13:19 +00:00
|
|
|
_planText("Monthly", _showYearlyPlan),
|
2021-08-20 12:06:05 +00:00
|
|
|
Switch(
|
|
|
|
value: _showYearlyPlan,
|
2021-08-23 10:22:07 +00:00
|
|
|
activeColor: Colors.white,
|
|
|
|
inactiveThumbColor: Colors.white,
|
2022-12-19 14:04:25 +00:00
|
|
|
activeTrackColor: getEnteColorScheme(context).strokeMuted,
|
2021-08-20 12:06:05 +00:00
|
|
|
onChanged: (value) async {
|
|
|
|
_showYearlyPlan = value;
|
2021-08-21 08:47:34 +00:00
|
|
|
await _filterStripeForUI();
|
2021-08-20 12:06:05 +00:00
|
|
|
},
|
|
|
|
),
|
2022-05-30 14:13:19 +00:00
|
|
|
_planText("Yearly", !_showYearlyPlan)
|
2021-08-20 12:06:05 +00:00
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _addCurrentPlanWidget(List<Widget> planWidgets) {
|
|
|
|
// don't add current plan if it's monthly plan but UI is showing yearly plans
|
|
|
|
// and vice versa.
|
2022-12-30 12:10:17 +00:00
|
|
|
if (_showYearlyPlan != _currentSubscription!.isYearlyPlan() &&
|
|
|
|
_currentSubscription!.productID != freeProductID) {
|
2021-08-20 12:06:05 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
int activePlanIndex = 0;
|
|
|
|
for (; activePlanIndex < _plans.length; activePlanIndex++) {
|
2022-12-30 12:10:17 +00:00
|
|
|
if (_plans[activePlanIndex].storage > _currentSubscription!.storage) {
|
2021-08-20 12:06:05 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
planWidgets.insert(
|
|
|
|
activePlanIndex,
|
|
|
|
Material(
|
|
|
|
child: InkWell(
|
|
|
|
onTap: () {},
|
|
|
|
child: SubscriptionPlanWidget(
|
2022-12-30 12:10:17 +00:00
|
|
|
storage: _currentSubscription!.storage,
|
|
|
|
price: _currentSubscription!.price,
|
|
|
|
period: _currentSubscription!.period,
|
2021-08-20 12:06:05 +00:00
|
|
|
isActive: true,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|