[mob][photos] Copy stripe subscription page changes to store subscription page
This commit is contained in:
@@ -1,55 +0,0 @@
|
||||
import "dart:async";
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
import 'package:photos/events/subscription_purchased_event.dart';
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import 'package:photos/models/billing_plan.dart';
|
||||
import 'package:photos/models/subscription.dart';
|
||||
import 'package:photos/services/billing_service.dart';
|
||||
import "package:photos/ui/tabs/home_widget.dart";
|
||||
|
||||
class SkipSubscriptionWidget extends StatelessWidget {
|
||||
const SkipSubscriptionWidget({
|
||||
Key? key,
|
||||
required this.freePlan,
|
||||
}) : super(key: key);
|
||||
|
||||
final FreePlan freePlan;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: 64,
|
||||
margin: const EdgeInsets.fromLTRB(0, 30, 0, 0),
|
||||
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
child: OutlinedButton(
|
||||
style: Theme.of(context).outlinedButtonTheme.style?.copyWith(
|
||||
textStyle: MaterialStateProperty.resolveWith<TextStyle>(
|
||||
(Set<MaterialState> states) {
|
||||
return Theme.of(context).textTheme.titleMedium!;
|
||||
},
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
Bus.instance.fire(SubscriptionPurchasedEvent());
|
||||
// ignore: unawaited_futures
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return const HomeWidget();
|
||||
},
|
||||
),
|
||||
(route) => false,
|
||||
);
|
||||
unawaited(
|
||||
BillingService.instance
|
||||
.verifySubscription(freeProductID, "", paymentProvider: "ente"),
|
||||
);
|
||||
},
|
||||
child: Text(S.of(context).continueOnFreeTrial),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -13,20 +13,20 @@ import 'package:photos/models/billing_plan.dart';
|
||||
import 'package:photos/models/subscription.dart';
|
||||
import 'package:photos/models/user_details.dart';
|
||||
import 'package:photos/services/billing_service.dart';
|
||||
import "package:photos/services/update_service.dart";
|
||||
import 'package:photos/services/user_service.dart';
|
||||
import "package:photos/theme/colors.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import 'package:photos/ui/common/loading_widget.dart';
|
||||
import 'package:photos/ui/common/progress_dialog.dart';
|
||||
import "package:photos/ui/components/captioned_text_widget.dart";
|
||||
import "package:photos/ui/components/divider_widget.dart";
|
||||
import "package:photos/ui/components/menu_item_widget/menu_item_widget.dart";
|
||||
import "package:photos/ui/components/title_bar_title_widget.dart";
|
||||
import 'package:photos/ui/payment/child_subscription_widget.dart';
|
||||
import 'package:photos/ui/payment/skip_subscription_widget.dart';
|
||||
import 'package:photos/ui/payment/subscription_common_widgets.dart';
|
||||
import 'package:photos/ui/payment/subscription_plan_widget.dart';
|
||||
import "package:photos/ui/payment/view_add_on_widget.dart";
|
||||
import "package:photos/ui/tabs/home_widget.dart";
|
||||
import "package:photos/utils/data_util.dart";
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
import 'package:photos/utils/toast_util.dart';
|
||||
@@ -69,9 +69,9 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_billingService.setIsOnSubscriptionPage(true);
|
||||
_setupPurchaseUpdateStreamListener();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void _setupPurchaseUpdateStreamListener() {
|
||||
@@ -162,13 +162,8 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
|
||||
_fetchSubData();
|
||||
}
|
||||
_dialog = createProgressDialog(context, S.of(context).pleaseWait);
|
||||
final appBar = AppBar(
|
||||
title: widget.isOnboarding
|
||||
? null
|
||||
: Text("${S.of(context).subscription}${kDebugMode ? ' Store' : ''}"),
|
||||
);
|
||||
return Scaffold(
|
||||
appBar: appBar,
|
||||
appBar: AppBar(),
|
||||
body: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -178,8 +173,9 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TitleBarTitleWidget(
|
||||
title:
|
||||
widget.isOnboarding ? "Select your plan" : "Subscription",
|
||||
title: widget.isOnboarding
|
||||
? "Select your plan"
|
||||
: "${S.of(context).subscription}${kDebugMode ? ' Store' : ''}",
|
||||
),
|
||||
_isFreePlanUser() || !_hasLoadedData
|
||||
? const SizedBox.shrink()
|
||||
@@ -192,7 +188,7 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
|
||||
],
|
||||
),
|
||||
),
|
||||
_getBody(),
|
||||
Expanded(child: _getBody()),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -259,6 +255,17 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
|
||||
),
|
||||
);
|
||||
|
||||
if (hasYearlyPlans) {
|
||||
widgets.add(
|
||||
SubscriptionToggle(
|
||||
onToggle: (p0) {
|
||||
showYearlyPlan = p0;
|
||||
_filterStorePlansForUi();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
widgets.addAll([
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@@ -266,13 +273,9 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
|
||||
? _getStripePlanWidgets()
|
||||
: _getMobilePlanWidgets(),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(8)),
|
||||
const Padding(padding: EdgeInsets.all(4)),
|
||||
]);
|
||||
|
||||
if (hasYearlyPlans) {
|
||||
widgets.add(_showSubscriptionToggle());
|
||||
}
|
||||
|
||||
if (_currentSubscription != null) {
|
||||
widgets.add(
|
||||
ValidityWidget(
|
||||
@@ -280,15 +283,11 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
|
||||
bonusData: _userDetails.bonusData,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (_currentSubscription!.productID == freeProductID) {
|
||||
if (widget.isOnboarding) {
|
||||
widgets.add(SkipSubscriptionWidget(freePlan: _freePlan));
|
||||
}
|
||||
widgets.add(
|
||||
SubFaqWidget(isOnboarding: widget.isOnboarding),
|
||||
);
|
||||
widgets.add(const DividerWidget(dividerType: DividerType.bottomBar));
|
||||
widgets.add(const SizedBox(height: 20));
|
||||
} else {
|
||||
widgets.add(const DividerWidget(dividerType: DividerType.bottomBar));
|
||||
const SizedBox(height: 56);
|
||||
}
|
||||
|
||||
if (_hasActiveSubscription &&
|
||||
@@ -311,7 +310,7 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
|
||||
padding: const EdgeInsets.fromLTRB(16, 40, 16, 4),
|
||||
child: MenuItemWidget(
|
||||
captionedTextWidget: CaptionedTextWidget(
|
||||
title: S.of(context).paymentDetails,
|
||||
title: "Manage payment method",
|
||||
),
|
||||
menuItemColor: colorScheme.fillFaint,
|
||||
trailingWidget: Icon(
|
||||
@@ -328,10 +327,15 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
widgets.add(
|
||||
SubFaqWidget(isOnboarding: widget.isOnboarding),
|
||||
);
|
||||
|
||||
if (!widget.isOnboarding) {
|
||||
widgets.add(
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 0),
|
||||
padding: const EdgeInsets.fromLTRB(16, 2, 16, 2),
|
||||
child: MenuItemWidget(
|
||||
captionedTextWidget: CaptionedTextWidget(
|
||||
title: _isFreePlanUser()
|
||||
@@ -354,8 +358,10 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
|
||||
),
|
||||
);
|
||||
widgets.add(ViewAddOnButton(_userDetails.bonusData));
|
||||
widgets.add(const SizedBox(height: 80));
|
||||
}
|
||||
|
||||
widgets.add(const SizedBox(height: 80));
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
@@ -411,64 +417,6 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Widget _showSubscriptionToggle() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(left: 8, right: 8, top: 2, bottom: 2),
|
||||
margin: const EdgeInsets.only(bottom: 6),
|
||||
child: Column(
|
||||
children: [
|
||||
RepaintBoundary(
|
||||
child: SizedBox(
|
||||
width: 250,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: SegmentedButton(
|
||||
style: SegmentedButton.styleFrom(
|
||||
selectedBackgroundColor:
|
||||
getEnteColorScheme(context).fillMuted,
|
||||
selectedForegroundColor:
|
||||
getEnteColorScheme(context).textBase,
|
||||
side: BorderSide(
|
||||
color: getEnteColorScheme(context).strokeMuted,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
segments: <ButtonSegment<bool>>[
|
||||
ButtonSegment(
|
||||
label: Text(S.of(context).monthly),
|
||||
value: false,
|
||||
),
|
||||
ButtonSegment(
|
||||
label: Text(S.of(context).yearly),
|
||||
value: true,
|
||||
),
|
||||
],
|
||||
selected: {showYearlyPlan},
|
||||
onSelectionChanged: (p0) {
|
||||
showYearlyPlan = p0.first;
|
||||
_filterStorePlansForUi();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
_isFreePlanUser() && !UpdateService.instance.isPlayStoreFlavor()
|
||||
? Text(
|
||||
S.of(context).twoMonthsFreeOnYearlyPlans,
|
||||
style: getEnteTextTheme(context).miniMuted,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
const Padding(padding: EdgeInsets.all(8)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _getStripePlanWidgets() {
|
||||
final List<Widget> planWidgets = [];
|
||||
bool foundActivePlan = false;
|
||||
@@ -483,10 +431,27 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
|
||||
foundActivePlan = true;
|
||||
}
|
||||
planWidgets.add(
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
if (widget.isOnboarding && plan.id == freeProductID) {
|
||||
Bus.instance.fire(SubscriptionPurchasedEvent());
|
||||
// ignore: unawaited_futures
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return const HomeWidget();
|
||||
},
|
||||
),
|
||||
(route) => false,
|
||||
);
|
||||
unawaited(
|
||||
BillingService.instance.verifySubscription(
|
||||
freeProductID,
|
||||
"",
|
||||
paymentProvider: "ente",
|
||||
),
|
||||
);
|
||||
} else {
|
||||
if (isActive) {
|
||||
return;
|
||||
}
|
||||
@@ -496,13 +461,15 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
|
||||
S.of(context).sorry,
|
||||
S.of(context).visitWebToManage,
|
||||
);
|
||||
},
|
||||
child: SubscriptionPlanWidget(
|
||||
storage: plan.storage,
|
||||
price: plan.price,
|
||||
period: plan.period,
|
||||
isActive: isActive && !_hideCurrentPlanSelection,
|
||||
),
|
||||
}
|
||||
},
|
||||
child: SubscriptionPlanWidget(
|
||||
storage: plan.storage,
|
||||
price: plan.price,
|
||||
period: plan.period,
|
||||
isActive: isActive && !_hideCurrentPlanSelection,
|
||||
isPopular: _isPopularPlan(plan),
|
||||
isOnboarding: widget.isOnboarding,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -522,9 +489,10 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
|
||||
planWidgets.add(
|
||||
SubscriptionPlanWidget(
|
||||
storage: _freePlan.storage,
|
||||
price: S.of(context).freeTrial,
|
||||
period: "",
|
||||
price: "",
|
||||
period: S.of(context).freeTrial,
|
||||
isActive: true,
|
||||
isOnboarding: widget.isOnboarding,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -536,71 +504,71 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
|
||||
foundActivePlan = true;
|
||||
}
|
||||
planWidgets.add(
|
||||
Material(
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
if (isActive) {
|
||||
return;
|
||||
}
|
||||
final int addOnBonus =
|
||||
_userDetails.bonusData?.totalAddOnBonus() ?? 0;
|
||||
if (_userDetails.getFamilyOrPersonalUsage() >
|
||||
(plan.storage + addOnBonus)) {
|
||||
_logger.warning(
|
||||
" familyUsage ${convertBytesToReadableFormat(_userDetails.getFamilyOrPersonalUsage())}"
|
||||
" plan storage ${convertBytesToReadableFormat(plan.storage)} "
|
||||
"addOnBonus ${convertBytesToReadableFormat(addOnBonus)},"
|
||||
"overshooting by ${convertBytesToReadableFormat(_userDetails.getFamilyOrPersonalUsage() - (plan.storage + addOnBonus))}",
|
||||
);
|
||||
// ignore: unawaited_futures
|
||||
showErrorDialog(
|
||||
context,
|
||||
S.of(context).sorry,
|
||||
S.of(context).youCannotDowngradeToThisPlan,
|
||||
);
|
||||
return;
|
||||
}
|
||||
await _dialog.show();
|
||||
final ProductDetailsResponse response =
|
||||
await InAppPurchase.instance.queryProductDetails({productID});
|
||||
if (response.notFoundIDs.isNotEmpty) {
|
||||
final errMsg = "Could not find products: " +
|
||||
response.notFoundIDs.toString();
|
||||
_logger.severe(errMsg);
|
||||
await _dialog.hide();
|
||||
await showGenericErrorDialog(
|
||||
context: context,
|
||||
error: Exception(errMsg),
|
||||
);
|
||||
return;
|
||||
}
|
||||
final isCrossGradingOnAndroid = Platform.isAndroid &&
|
||||
_hasActiveSubscription &&
|
||||
_currentSubscription!.productID != freeProductID &&
|
||||
_currentSubscription!.productID != plan.androidID;
|
||||
if (isCrossGradingOnAndroid) {
|
||||
await _dialog.hide();
|
||||
// ignore: unawaited_futures
|
||||
showErrorDialog(
|
||||
context,
|
||||
S.of(context).couldNotUpdateSubscription,
|
||||
S.of(context).pleaseContactSupportAndWeWillBeHappyToHelp,
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
await InAppPurchase.instance.buyNonConsumable(
|
||||
purchaseParam: PurchaseParam(
|
||||
productDetails: response.productDetails[0],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: SubscriptionPlanWidget(
|
||||
storage: plan.storage,
|
||||
price: plan.price,
|
||||
period: plan.period,
|
||||
isActive: isActive,
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
if (isActive) {
|
||||
return;
|
||||
}
|
||||
final int addOnBonus =
|
||||
_userDetails.bonusData?.totalAddOnBonus() ?? 0;
|
||||
if (_userDetails.getFamilyOrPersonalUsage() >
|
||||
(plan.storage + addOnBonus)) {
|
||||
_logger.warning(
|
||||
" familyUsage ${convertBytesToReadableFormat(_userDetails.getFamilyOrPersonalUsage())}"
|
||||
" plan storage ${convertBytesToReadableFormat(plan.storage)} "
|
||||
"addOnBonus ${convertBytesToReadableFormat(addOnBonus)},"
|
||||
"overshooting by ${convertBytesToReadableFormat(_userDetails.getFamilyOrPersonalUsage() - (plan.storage + addOnBonus))}",
|
||||
);
|
||||
// ignore: unawaited_futures
|
||||
showErrorDialog(
|
||||
context,
|
||||
S.of(context).sorry,
|
||||
S.of(context).youCannotDowngradeToThisPlan,
|
||||
);
|
||||
return;
|
||||
}
|
||||
await _dialog.show();
|
||||
final ProductDetailsResponse response =
|
||||
await InAppPurchase.instance.queryProductDetails({productID});
|
||||
if (response.notFoundIDs.isNotEmpty) {
|
||||
final errMsg =
|
||||
"Could not find products: " + response.notFoundIDs.toString();
|
||||
_logger.severe(errMsg);
|
||||
await _dialog.hide();
|
||||
await showGenericErrorDialog(
|
||||
context: context,
|
||||
error: Exception(errMsg),
|
||||
);
|
||||
return;
|
||||
}
|
||||
final isCrossGradingOnAndroid = Platform.isAndroid &&
|
||||
_hasActiveSubscription &&
|
||||
_currentSubscription!.productID != freeProductID &&
|
||||
_currentSubscription!.productID != plan.androidID;
|
||||
if (isCrossGradingOnAndroid) {
|
||||
await _dialog.hide();
|
||||
// ignore: unawaited_futures
|
||||
showErrorDialog(
|
||||
context,
|
||||
S.of(context).couldNotUpdateSubscription,
|
||||
S.of(context).pleaseContactSupportAndWeWillBeHappyToHelp,
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
await InAppPurchase.instance.buyNonConsumable(
|
||||
purchaseParam: PurchaseParam(
|
||||
productDetails: response.productDetails[0],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: SubscriptionPlanWidget(
|
||||
storage: plan.storage,
|
||||
price: plan.price,
|
||||
period: plan.period,
|
||||
isActive: isActive,
|
||||
isPopular: _isPopularPlan(plan),
|
||||
isOnboarding: widget.isOnboarding,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -620,17 +588,40 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
|
||||
}
|
||||
planWidgets.insert(
|
||||
activePlanIndex,
|
||||
Material(
|
||||
child: InkWell(
|
||||
onTap: () {},
|
||||
child: SubscriptionPlanWidget(
|
||||
storage: _currentSubscription!.storage,
|
||||
price: _currentSubscription!.price,
|
||||
period: _currentSubscription!.period,
|
||||
isActive: true,
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (_currentSubscription!.isFreePlan()) {
|
||||
Bus.instance.fire(SubscriptionPurchasedEvent());
|
||||
// ignore: unawaited_futures
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return const HomeWidget();
|
||||
},
|
||||
),
|
||||
(route) => false,
|
||||
);
|
||||
unawaited(
|
||||
BillingService.instance.verifySubscription(
|
||||
freeProductID,
|
||||
"",
|
||||
paymentProvider: "ente",
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: SubscriptionPlanWidget(
|
||||
storage: _currentSubscription!.storage,
|
||||
price: _currentSubscription!.price,
|
||||
period: _currentSubscription!.period,
|
||||
isActive: true,
|
||||
isOnboarding: widget.isOnboarding,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
bool _isPopularPlan(BillingPlan plan) {
|
||||
return popularProductIDs.contains(plan.id);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user