diff --git a/lib/screens/lightmeter_pro/components/offering/widget_offering_lightmeter_pro.dart b/lib/screens/lightmeter_pro/components/offering/widget_offering_lightmeter_pro.dart index 6511a3f..b3a8f6d 100644 --- a/lib/screens/lightmeter_pro/components/offering/widget_offering_lightmeter_pro.dart +++ b/lib/screens/lightmeter_pro/components/offering/widget_offering_lightmeter_pro.dart @@ -8,9 +8,11 @@ import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; class LightmeterProOffering extends StatefulWidget { const LightmeterProOffering({ super.key, + required this.isEnabled, required this.onBuyProduct, }); + final bool isEnabled; final ValueChanged onBuyProduct; @override @@ -49,19 +51,26 @@ class _LightmeterProOfferingState extends State { mainAxisSize: MainAxisSize.min, children: [ if (!_isLifetimeOnly) - Padding( - padding: const EdgeInsets.only(bottom: Dimens.paddingS), - child: _Products( - monthly: IAPProducts.of(context).monthly, - yearly: IAPProducts.of(context).yearly, - lifetime: IAPProducts.of(context).lifetime, - selected: selected, - onProductSelected: _selectProduct, + AnimatedOpacity( + duration: Dimens.durationM, + opacity: widget.isEnabled ? Dimens.enabledOpacity : Dimens.disabledOpacity, + child: IgnorePointer( + ignoring: !widget.isEnabled, + child: Padding( + padding: const EdgeInsets.only(bottom: Dimens.paddingS), + child: _Products( + monthly: IAPProducts.of(context).monthly, + yearly: IAPProducts.of(context).yearly, + lifetime: IAPProducts.of(context).lifetime, + selected: selected, + onProductSelected: _selectProduct, + ), + ), ), ), FilledButtonLarge( title: S.of(context).continuePurchase, - onPressed: selected != null ? () => widget.onBuyProduct(selected!) : null, + onPressed: widget.isEnabled && selected != null ? () => widget.onBuyProduct(selected!) : null, ), ], ), diff --git a/lib/screens/lightmeter_pro/screen_lightmeter_pro.dart b/lib/screens/lightmeter_pro/screen_lightmeter_pro.dart index 0edc800..00ba81d 100644 --- a/lib/screens/lightmeter_pro/screen_lightmeter_pro.dart +++ b/lib/screens/lightmeter_pro/screen_lightmeter_pro.dart @@ -9,6 +9,8 @@ import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart'; import 'package:lightmeter/utils/text_height.dart'; import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; +typedef PurchasesState = ({bool isPurchasingProduct, bool isRestoringPurchases}); + class LightmeterProScreen extends StatefulWidget { const LightmeterProScreen({super.key}); @@ -20,19 +22,34 @@ class _LightmeterProScreenState extends State { final features = defaultTargetPlatform == TargetPlatform.android ? AppFeature.androidFeatures : AppFeature.iosFeatures; - final _isRestoringPurchases = ValueNotifier(false); - final _isPurchasingProduct = ValueNotifier(false); + final _purchasesNotifier = ValueNotifier( + ( + isPurchasingProduct: false, + isRestoringPurchases: false, + ), + ); @override Widget build(BuildContext context) { return SliverScreen( title: Text(S.of(context).proFeaturesTitle), appBarActions: [ - /// Restoration is working, but it does not trigger pop - /// add await of didChangeDependencies - _RestorePurchasesButton( - valueListenable: _isRestoringPurchases, - onPressed: _restorePurchases, + ValueListenableBuilder( + valueListenable: _purchasesNotifier, + builder: (context, value, _) { + if (value.isRestoringPurchases) { + return const SizedBox.square( + dimension: Dimens.grid24 - Dimens.grid4, + child: CircularProgressIndicator(), + ); + } else { + return IconButton( + onPressed: value.isPurchasingProduct ? null : _restorePurchases, + icon: const Icon(Icons.restore), + tooltip: S.of(context).restorePurchases, + ); + } + }, ), ], slivers: [ @@ -75,12 +92,26 @@ class _LightmeterProScreenState extends State { ), ), ], - bottomNavigationBar: LightmeterProOffering(onBuyProduct: _buyPro), + bottomNavigationBar: ValueListenableBuilder( + valueListenable: _purchasesNotifier, + builder: (context, value, _) { + return LightmeterProOffering( + isEnabled: !value.isRestoringPurchases && !value.isPurchasingProduct, + onBuyProduct: _buyPro, + ); + }, + ), ); } + @override + void dispose() { + _purchasesNotifier.dispose(); + super.dispose(); + } + Future _restorePurchases() async { - _isRestoringPurchases.value = true; + _purchasesNotifier.isRestoringPurchases = true; try { final isPro = await IAPProductsProvider.of(context).restorePurchases(); if (mounted && isPro) { @@ -91,12 +122,12 @@ class _LightmeterProScreenState extends State { } catch (e) { _showSnackbar(e.toString()); } finally { - _isRestoringPurchases.value = true; + _purchasesNotifier.isRestoringPurchases = false; } } Future _buyPro(IAPProduct product) async { - _isPurchasingProduct.value = true; + _purchasesNotifier.isPurchasingProduct = true; try { final isPro = await IAPProductsProvider.of(context).buyPro(product); if (mounted && isPro) { @@ -107,7 +138,7 @@ class _LightmeterProScreenState extends State { } catch (e) { _showSnackbar(e.toString()); } finally { - _isPurchasingProduct.value = false; + _purchasesNotifier.isPurchasingProduct = false; } } @@ -123,37 +154,6 @@ class _LightmeterProScreenState extends State { } } -class _RestorePurchasesButton extends StatelessWidget { - const _RestorePurchasesButton({ - required this.valueListenable, - required this.onPressed, - }); - - final ValueNotifier valueListenable; - final VoidCallback onPressed; - - @override - Widget build(BuildContext context) { - return ValueListenableBuilder( - valueListenable: valueListenable, - builder: (context, value, _) { - if (value) { - return const SizedBox.square( - dimension: Dimens.grid24 - Dimens.grid4, - child: CircularProgressIndicator(), - ); - } else { - return IconButton( - onPressed: onPressed, - icon: const Icon(Icons.restore), - tooltip: S.of(context).restorePurchases, - ); - } - }, - ); - } -} - class _FeaturesHeader extends StatelessWidget { const _FeaturesHeader(); @@ -292,3 +292,13 @@ class _CheckBox extends StatelessWidget { ); } } + +extension on ValueNotifier { + set isPurchasingProduct(bool isPurchasingProduct) { + value = (isPurchasingProduct: isPurchasingProduct, isRestoringPurchases: value.isRestoringPurchases); + } + + set isRestoringPurchases(bool isRestoringPurchases) { + value = (isPurchasingProduct: value.isPurchasingProduct, isRestoringPurchases: isRestoringPurchases); + } +}