From 0fbf252d9e65f9344ae0bfbe4c8af50eeb64eff0 Mon Sep 17 00:00:00 2001 From: Vadim <44135514+vodemn@users.noreply.github.com> Date: Sun, 17 Sep 2023 22:29:02 +0200 Subject: [PATCH] ML-117 Improve description of paid features (#119) * wip * added `LightmeterProSettingsSection` * hide Pro section on purchase * `ElevatedButton` -> `FilledButton` * moved Pro description to iap/README.md * intl * disable only list tiles * show iap dialog on every iap list tile --- iap/README.md | 17 +++++++++ lib/l10n/intl_en.arb | 8 +++-- lib/l10n/intl_fr.arb | 8 +++-- lib/l10n/intl_ru.arb | 8 +++-- lib/l10n/intl_zh.arb | 8 +++-- .../buy_pro/widget_list_tile_buy_pro.dart | 20 +++++++++++ ...idget_settings_section_lightmeter_pro.dart | 16 +++++++++ .../iap_list_tile/widget_list_tile_iap.dart | 17 +++++++-- .../widget_settings_section.dart | 35 ++++++++----------- .../components/utils/show_buy_pro_dialog.dart | 31 ++++++++++++++++ lib/screens/settings/screen_settings.dart | 4 +++ 11 files changed, 142 insertions(+), 30 deletions(-) create mode 100644 iap/README.md create mode 100644 lib/screens/settings/components/lightmeter_pro/components/buy_pro/widget_list_tile_buy_pro.dart create mode 100644 lib/screens/settings/components/lightmeter_pro/widget_settings_section_lightmeter_pro.dart create mode 100644 lib/screens/settings/components/utils/show_buy_pro_dialog.dart diff --git a/iap/README.md b/iap/README.md new file mode 100644 index 0000000..d978bb0 --- /dev/null +++ b/iap/README.md @@ -0,0 +1,17 @@ +# Lightmeter Pro + +### Equipment profiles + +Each equipment profile allows you to select: + +- Aperture values and shutter speeds, that your lens and camera have +- ND filters, that fit the chosen lens +- ISO values, that your camera supports + +Creating multiple profiles for different cameras and lenses allows you to easily switch between them and always have the relevant readings. + +### Films in use + +Select the films that you usually use. Selecting one will apply a correction to shutter speeds greater than 1" to compensate for the reciprocity failure. + +Each equipment profile allows you to select:\n- Aperture values and shutter speeds, that your lens and camera have\n- ND filters, that fit the chosen lens\n- ISO values, that your camera supports\nCreating multiple profiles for different cameras and lenses allows you to easily switch between them and always have the relevant readings! \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 364deec..4e12ad8 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -92,5 +92,9 @@ "type": "String" } } - } -} + }, + "buyLightmeterPro": "Buy Lightmeter Pro", + "lightmeterPro": "Lightmeter Pro", + "lightmeterProDescription": "Unlocks extra features, such as equipment profiles containing filters for aperture, shutter speed, and more; and a list of films with compensation for what's known as reciprocity failure.\n\nThe source code of Lightmeter is available on GitHub. You are welcome to compile it yourself. However, if you want to support the development and receive new features and updates, consider purchasing Lightmeter Pro.", + "buy": "Buy" +} \ No newline at end of file diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 22b0356..5946d5b 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -92,5 +92,9 @@ "type": "String" } } - } -} + }, + "buyLightmeterPro": "Acheter Lightmeter Pro", + "lightmeterPro": "Lightmeter Pro", + "lightmeterProDescription": "Déverrouille des fonctionnalités supplémentaires, telles que des profils d'équipement contenant des filtres pour l'ouverture, la vitesse d'obturation et plus encore, ainsi qu'une liste de films avec une compensation pour ce que l'on appelle l'échec de réciprocité.\n\nLe code source du Lightmeter est disponible sur GitHub. Vous pouvez le compiler vous-même. Cependant, si vous souhaitez soutenir le développement et recevoir de nouvelles fonctionnalités et mises à jour, envisagez d'acheter Lightmeter Pro.", + "buy": "Acheter" +} \ No newline at end of file diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index 9ce0d9b..9f428c4 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -92,5 +92,9 @@ "type": "String" } } - } -} + }, + "buyLightmeterPro": "Купить Lightmeter Pro", + "lightmeterPro": "Lightmeter Pro", + "lightmeterProDescription": "Даёт доступ к таким функциям как профили оборудования, содержащие фильтры для диафрагмы, выдержки и других значений, а также набору пленок с компенсацией эффекта Шварцшильда.\n\nИсходный код Lightmeter доступен на GitHub. Вы можете собрать его самостоятельно. Однако если вы хотите поддержать разработку и получать новые функции и обновления, то приобретите Lightmeter Pro.", + "buy": "Купить" +} \ No newline at end of file diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index 357ef95..92f19ee 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -92,5 +92,9 @@ "type": "String" } } - } -} + }, + "buyLightmeterPro": "Buy Lightmeter Pro", + "lightmeterPro": "Lightmeter Pro", + "lightmeterProDescription": "Unlocks extra features, such as equipment profiles containing filters for aperture, shutter speed, and more; and a list of films with compensation for what's known as reciprocity failure.\n\nThe source code of Lightmeter is available on GitHub. You are welcome to compile it yourself. However, if you want to support the development and receive new features and updates, consider purchasing Lightmeter Pro.", + "buy": "Buy" +} \ No newline at end of file diff --git a/lib/screens/settings/components/lightmeter_pro/components/buy_pro/widget_list_tile_buy_pro.dart b/lib/screens/settings/components/lightmeter_pro/components/buy_pro/widget_list_tile_buy_pro.dart new file mode 100644 index 0000000..11e8a4f --- /dev/null +++ b/lib/screens/settings/components/lightmeter_pro/components/buy_pro/widget_list_tile_buy_pro.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart'; +import 'package:lightmeter/screens/settings/components/utils/show_buy_pro_dialog.dart'; + +class BuyProListTile extends StatelessWidget { + const BuyProListTile({super.key}); + + @override + Widget build(BuildContext context) { + return IAPListTile( + leading: const Icon(Icons.star), + title: Text(S.of(context).buyLightmeterPro), + onTap: () { + showBuyProDialog(context); + }, + showPendingTrailing: true, + ); + } +} diff --git a/lib/screens/settings/components/lightmeter_pro/widget_settings_section_lightmeter_pro.dart b/lib/screens/settings/components/lightmeter_pro/widget_settings_section_lightmeter_pro.dart new file mode 100644 index 0000000..c060dc1 --- /dev/null +++ b/lib/screens/settings/components/lightmeter_pro/widget_settings_section_lightmeter_pro.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/settings/components/lightmeter_pro/components/buy_pro/widget_list_tile_buy_pro.dart'; +import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart'; + +class LightmeterProSettingsSection extends StatelessWidget { + const LightmeterProSettingsSection({super.key}); + + @override + Widget build(BuildContext context) { + return SettingsSection( + title: S.of(context).lightmeterPro, + children: const [BuyProListTile()], + ); + } +} diff --git a/lib/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart b/lib/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart index 8c79f37..cf65ade 100644 --- a/lib/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart +++ b/lib/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:lightmeter/res/dimens.dart'; +import 'package:lightmeter/screens/settings/components/utils/show_buy_pro_dialog.dart'; import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; /// Depends on the product status and replaces [onTap] with purchase callback @@ -8,26 +10,37 @@ class IAPListTile extends StatelessWidget { final Icon leading; final Text title; final VoidCallback onTap; + final bool showPendingTrailing; const IAPListTile({ this.product = IAPProductType.paidFeatures, required this.leading, required this.title, required this.onTap, + this.showPendingTrailing = false, super.key, }); @override Widget build(BuildContext context) { + final status = IAPProducts.productOf(context, IAPProductType.paidFeatures)?.status; + final isPending = status == IAPProductStatus.purchased || status == null; return ListTile( leading: leading, title: title, - onTap: switch (IAPProducts.productOf(context, product)?.status) { - IAPProductStatus.purchasable => () => IAPProductsProvider.of(context).buy(product), + onTap: switch (status) { + IAPProductStatus.purchasable => () => showBuyProDialog(context), IAPProductStatus.pending => null, IAPProductStatus.purchased => onTap, null => null, }, + trailing: showPendingTrailing && isPending + ? const SizedBox( + height: Dimens.grid24, + width: Dimens.grid24, + child: CircularProgressIndicator(), + ) + : null, ); } } diff --git a/lib/screens/settings/components/shared/settings_section/widget_settings_section.dart b/lib/screens/settings/components/shared/settings_section/widget_settings_section.dart index 9f88d7d..be62847 100644 --- a/lib/screens/settings/components/shared/settings_section/widget_settings_section.dart +++ b/lib/screens/settings/components/shared/settings_section/widget_settings_section.dart @@ -4,12 +4,10 @@ import 'package:lightmeter/res/dimens.dart'; class SettingsSection extends StatelessWidget { final String title; final List children; - final bool enabled; const SettingsSection({ required this.title, required this.children, - this.enabled = true, super.key, }); @@ -25,25 +23,22 @@ class SettingsSection extends StatelessWidget { child: Card( child: Padding( padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM), - child: Opacity( - opacity: enabled ? Dimens.enabledOpacity : Dimens.disabledOpacity, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM), - child: Text( - title, - style: Theme.of(context) - .textTheme - .labelLarge - ?.copyWith(color: Theme.of(context).colorScheme.onSurface), - ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM), + child: Text( + title, + style: Theme.of(context) + .textTheme + .labelLarge + ?.copyWith(color: Theme.of(context).colorScheme.onSurface), ), - ...children, - ], - ), + ), + ...children, + ], ), ), ), diff --git a/lib/screens/settings/components/utils/show_buy_pro_dialog.dart b/lib/screens/settings/components/utils/show_buy_pro_dialog.dart new file mode 100644 index 0000000..0333570 --- /dev/null +++ b/lib/screens/settings/components/utils/show_buy_pro_dialog.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/res/dimens.dart'; +import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; + +Future showBuyProDialog(BuildContext context) { + return showDialog( + context: context, + builder: (_) => AlertDialog( + icon: const Icon(Icons.star), + titlePadding: Dimens.dialogIconTitlePadding, + title: Text(S.of(context).lightmeterPro), + contentPadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingL), + content: SingleChildScrollView(child: Text(S.of(context).lightmeterProDescription)), + actionsPadding: Dimens.dialogActionsPadding, + actions: [ + TextButton( + onPressed: Navigator.of(context).pop, + child: Text(S.of(context).cancel), + ), + FilledButton( + onPressed: () { + Navigator.of(context).pop(); + IAPProductsProvider.of(context).buy(IAPProductType.paidFeatures); + }, + child: Text(S.of(context).buy), + ), + ], + ), + ); +} diff --git a/lib/screens/settings/screen_settings.dart b/lib/screens/settings/screen_settings.dart index 7d057cf..3f1c0a6 100644 --- a/lib/screens/settings/screen_settings.dart +++ b/lib/screens/settings/screen_settings.dart @@ -3,10 +3,12 @@ import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/screens/settings/components/about/widget_settings_section_about.dart'; import 'package:lightmeter/screens/settings/components/equipment/widget_settings_section_equipment.dart'; import 'package:lightmeter/screens/settings/components/general/widget_settings_section_general.dart'; +import 'package:lightmeter/screens/settings/components/lightmeter_pro/widget_settings_section_lightmeter_pro.dart'; import 'package:lightmeter/screens/settings/components/metering/widget_settings_section_metering.dart'; import 'package:lightmeter/screens/settings/components/theme/widget_settings_section_theme.dart'; import 'package:lightmeter/screens/settings/flow_settings.dart'; import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart'; +import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; class SettingsScreen extends StatefulWidget { const SettingsScreen({super.key}); @@ -43,6 +45,8 @@ class _SettingsScreenState extends State { SliverList( delegate: SliverChildListDelegate( [ + if (!IAPProducts.isPurchased(context, IAPProductType.paidFeatures)) + const LightmeterProSettingsSection(), const MeteringSettingsSection(), const EquipmentSettingsSection(), const GeneralSettingsSection(),