Apple IAP

This commit is contained in:
Hillel Coren 2022-07-13 17:25:01 +03:00
parent cb39924f19
commit ca9541026b
3 changed files with 67 additions and 49 deletions

View File

@ -61,6 +61,8 @@ PODS:
- TOCropViewController (~> 2.6.1) - TOCropViewController (~> 2.6.1)
- image_picker_ios (0.0.1): - image_picker_ios (0.0.1):
- Flutter - Flutter
- in_app_purchase_storekit (0.0.1):
- Flutter
- in_app_review (0.2.0): - in_app_review (0.2.0):
- Flutter - Flutter
- local_auth (0.0.1): - local_auth (0.0.1):
@ -108,6 +110,7 @@ DEPENDENCIES:
- google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/ios`) - google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/ios`)
- image_cropper (from `.symlinks/plugins/image_cropper/ios`) - image_cropper (from `.symlinks/plugins/image_cropper/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/ios`)
- in_app_review (from `.symlinks/plugins/in_app_review/ios`) - in_app_review (from `.symlinks/plugins/in_app_review/ios`)
- local_auth (from `.symlinks/plugins/local_auth/ios`) - local_auth (from `.symlinks/plugins/local_auth/ios`)
- package_info (from `.symlinks/plugins/package_info/ios`) - package_info (from `.symlinks/plugins/package_info/ios`)
@ -150,6 +153,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/image_cropper/ios" :path: ".symlinks/plugins/image_cropper/ios"
image_picker_ios: image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios" :path: ".symlinks/plugins/image_picker_ios/ios"
in_app_purchase_storekit:
:path: ".symlinks/plugins/in_app_purchase_storekit/ios"
in_app_review: in_app_review:
:path: ".symlinks/plugins/in_app_review/ios" :path: ".symlinks/plugins/in_app_review/ios"
local_auth: local_auth:
@ -193,6 +198,7 @@ SPEC CHECKSUMS:
GTMSessionFetcher: 5595ec75acf5be50814f81e9189490412bad82ba GTMSessionFetcher: 5595ec75acf5be50814f81e9189490412bad82ba
image_cropper: 60c2789d1f1a78c873235d4319ca0c34a69f2d98 image_cropper: 60c2789d1f1a78c873235d4319ca0c34a69f2d98
image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb
in_app_purchase_storekit: d7fcf4646136ec258e237872755da8ea6c1b6096
in_app_review: 4a97249f7a2f539a0f294c2d9196b7fe35e49541 in_app_review: 4a97249f7a2f539a0f294c2d9196b7fe35e49541
local_auth: 1740f55d7af0a2e2a8684ce225fe79d8931e808c local_auth: 1740f55d7af0a2e2a8684ce225fe79d8931e808c
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62

View File

@ -11,6 +11,7 @@ import 'package:flutter_redux/flutter_redux.dart';
import 'package:flutter_styled_toast/flutter_styled_toast.dart'; import 'package:flutter_styled_toast/flutter_styled_toast.dart';
import 'package:invoiceninja_flutter/redux/auth/auth_actions.dart'; import 'package:invoiceninja_flutter/redux/auth/auth_actions.dart';
import 'package:invoiceninja_flutter/redux/settings/settings_actions.dart'; import 'package:invoiceninja_flutter/redux/settings/settings_actions.dart';
import 'package:invoiceninja_flutter/ui/app/upgrade_dialog.dart';
import 'package:invoiceninja_flutter/utils/app_review.dart'; import 'package:invoiceninja_flutter/utils/app_review.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart';
@ -383,7 +384,7 @@ class MenuDrawer extends StatelessWidget {
}, },
), ),
), ),
if (state.userCompany.isOwner && if (true || state.userCompany.isOwner &&
state.isHosted && state.isHosted &&
!isPaidAccount(context) && !isPaidAccount(context) &&
!isApple() && !isApple() &&
@ -397,15 +398,14 @@ class MenuDrawer extends StatelessWidget {
dense: true, dense: true,
tileColor: Colors.green, tileColor: Colors.green,
leading: Padding( leading: Padding(
padding: const EdgeInsets.only(left: 9), padding: const EdgeInsets.only(left: 6),
child: Icon( child: Icon(
Icons.arrow_circle_up, Icons.arrow_circle_up,
size: 22, size: 22,
color: Colors.white, color: Colors.white,
), ),
), ),
contentPadding: contentPadding: const EdgeInsets.only(left: 20),
const EdgeInsets.only(left: 12),
title: state.isMenuCollapsed title: state.isMenuCollapsed
? SizedBox() ? SizedBox()
: Text( : Text(
@ -419,11 +419,16 @@ class MenuDrawer extends StatelessWidget {
), ),
), ),
onTap: () { onTap: () {
showDialog<void>(context: context, builder: (BuildContext context) => UpgradeDialog());
/*
store.dispatch(ViewSettings( store.dispatch(ViewSettings(
clearFilter: true, clearFilter: true,
company: company, company: company,
user: state.user, user: state.user,
section: kSettingsAccountManagement)); section: kSettingsAccountManagement));
*/
}, },
), ),
), ),

View File

@ -13,6 +13,7 @@ import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart';
import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; import 'package:in_app_purchase_storekit/store_kit_wrappers.dart';
import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/constants.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/ui/dashboard/dashboard_chart.dart';
class UpgradeDialog extends StatefulWidget { class UpgradeDialog extends StatefulWidget {
@override @override
@ -22,7 +23,6 @@ class UpgradeDialog extends StatefulWidget {
class _UpgradeDialogState extends State<UpgradeDialog> { class _UpgradeDialogState extends State<UpgradeDialog> {
final InAppPurchase _inAppPurchase = InAppPurchase.instance; final InAppPurchase _inAppPurchase = InAppPurchase.instance;
StreamSubscription<List<PurchaseDetails>> _subscription; StreamSubscription<List<PurchaseDetails>> _subscription;
List<String> _notFoundIds = <String>[];
List<ProductDetails> _products = <ProductDetails>[]; List<ProductDetails> _products = <ProductDetails>[];
List<PurchaseDetails> _purchases = <PurchaseDetails>[]; List<PurchaseDetails> _purchases = <PurchaseDetails>[];
bool _isAvailable = false; bool _isAvailable = false;
@ -53,7 +53,6 @@ class _UpgradeDialogState extends State<UpgradeDialog> {
_isAvailable = isAvailable; _isAvailable = isAvailable;
_products = <ProductDetails>[]; _products = <ProductDetails>[];
_purchases = <PurchaseDetails>[]; _purchases = <PurchaseDetails>[];
_notFoundIds = <String>[];
_purchasePending = false; _purchasePending = false;
_loading = false; _loading = false;
}); });
@ -75,7 +74,6 @@ class _UpgradeDialogState extends State<UpgradeDialog> {
_isAvailable = isAvailable; _isAvailable = isAvailable;
_products = productDetailResponse.productDetails; _products = productDetailResponse.productDetails;
_purchases = <PurchaseDetails>[]; _purchases = <PurchaseDetails>[];
_notFoundIds = productDetailResponse.notFoundIDs;
_purchasePending = false; _purchasePending = false;
_loading = false; _loading = false;
}); });
@ -88,7 +86,6 @@ class _UpgradeDialogState extends State<UpgradeDialog> {
_isAvailable = isAvailable; _isAvailable = isAvailable;
_products = productDetailResponse.productDetails; _products = productDetailResponse.productDetails;
_purchases = <PurchaseDetails>[]; _purchases = <PurchaseDetails>[];
_notFoundIds = productDetailResponse.notFoundIDs;
_purchasePending = false; _purchasePending = false;
_loading = false; _loading = false;
}); });
@ -98,7 +95,6 @@ class _UpgradeDialogState extends State<UpgradeDialog> {
setState(() { setState(() {
_isAvailable = isAvailable; _isAvailable = isAvailable;
_products = productDetailResponse.productDetails; _products = productDetailResponse.productDetails;
_notFoundIds = productDetailResponse.notFoundIDs;
_purchasePending = false; _purchasePending = false;
_loading = false; _loading = false;
}); });
@ -150,7 +146,7 @@ class _UpgradeDialogState extends State<UpgradeDialog> {
); );
} }
return Stack(children: stack); return AlertDialog(content: Stack(children: stack));
} }
Card _buildConnectionCheckTile() { Card _buildConnectionCheckTile() {
@ -194,13 +190,7 @@ class _UpgradeDialogState extends State<UpgradeDialog> {
final store = StoreProvider.of<AppState>(context); final store = StoreProvider.of<AppState>(context);
final account = store.state.account; final account = store.state.account;
if (_notFoundIds.isNotEmpty) {
productList.add(ListTile(
title: Text('[${_notFoundIds.join(", ")}] not found',
style: TextStyle(color: ThemeData.light().errorColor)),
subtitle: const Text(
'This app needs special configuration to run. Please see example/README.md for instructions.')));
}
// This loading previous purchases code is just a demo. Please do not use this as it is. // This loading previous purchases code is just a demo. Please do not use this as it is.
// In your app you should always verify the purchase data using the `verificationData` inside the [PurchaseDetails] object before trusting it. // In your app you should always verify the purchase data using the `verificationData` inside the [PurchaseDetails] object before trusting it.
@ -216,14 +206,28 @@ class _UpgradeDialogState extends State<UpgradeDialog> {
productList.addAll(_products.map( productList.addAll(_products.map(
(ProductDetails productDetails) { (ProductDetails productDetails) {
final PurchaseDetails previousPurchase = purchases[productDetails.id]; final PurchaseDetails previousPurchase = purchases[productDetails.id];
String title = productDetails.title;
String description = productDetails.description;
// TODO remove this code
// Workaround for product in app store with blank values
if (title.isEmpty && productDetails.id == kProductEnterprisePlanMonth_10) {
title = 'Enterprise - Month (6-10)';
description = 'One month of the Enterprise Plan (10 users)';
}
return ListTile( return ListTile(
title: Text( title: Text(
productDetails.title, title
), ),
subtitle: Text( subtitle: Column(
productDetails.description, crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
description
), ),
trailing: previousPurchase != null previousPurchase != null
? IconButton( ? IconButton(
onPressed: () => confirmPriceChange(context), onPressed: () => confirmPriceChange(context),
icon: const Icon(Icons.upgrade)) icon: const Icon(Icons.upgrade))
@ -254,6 +258,9 @@ class _UpgradeDialogState extends State<UpgradeDialog> {
}, },
child: Text(productDetails.price), child: Text(productDetails.price),
), ),
SizedBox(height: 20),
],
),
); );
}, },
)); ));