import 'package:guru_app/analytics/guru_analytics.dart'; import 'package:guru_app/database/guru_db.dart'; import 'package:guru_app/financial/asset/assets_model.dart'; import 'package:guru_app/financial/asset/assets_store.dart'; import 'package:guru_app/financial/data/db/order_database.dart'; import 'package:guru_app/financial/igc/igc_model.dart'; import 'package:guru_app/financial/manifest/manifest_manager.dart'; import 'package:guru_app/financial/product/product_model.dart'; import 'package:guru_app/guru_app.dart'; import 'package:guru_app/property/app_property.dart'; import 'package:guru_utils/extensions/extensions.dart'; import 'package:guru_utils/log/log.dart'; import 'package:guru_utils/property/app_property.dart'; /// Created by Haoyi on 2023/2/18 class IgcManager { static final IgcManager _instance = IgcManager._(); static IgcManager get instance => _instance; final BehaviorSubject _balanceSubject = BehaviorSubject.seeded(0); final BehaviorSubject> _assetStoreSubject = BehaviorSubject.seeded(AssetsStore.inactive()); Stream> get observableAssetStore => _assetStoreSubject.stream; AssetsStore get purchasedStore => _assetStoreSubject.value; int get currentBalance => _balanceSubject.value; Stream get observableCurrentBalance => _balanceSubject.stream; final CompositeSubscription subscriptions = CompositeSubscription(); IgcManager._(); Future init() async { final attachedGems = (await AppProperty.getInstance().isFirstUseGemsFeature()) ? GuruApp.instance.appSpec.deployment.initIgc : 0; final _balance = await AppProperty.getInstance().accumulateIgc(attachedGems); _balanceSubject.addEx(_balance); GuruAnalytics.instance.setUserProperty("coin", _balance.toString()); final iapIgc = await AppProperty.getInstance().getIapIgc(); GuruAnalytics.instance.setUserProperty("iap_coin", iapIgc.toString()); final noIapIgcGems = await AppProperty.getInstance().getNoIapIgc(); GuruAnalytics.instance.setUserProperty("noniap_coin", noIapIgcGems.toString()); await reloadAssets(); } Future switchSession() async { init(); } Future reloadAssets() async { final orders = await GuruDB.instance .selectOrders(method: TransactionMethod.igc, attrs: [TransactionAttributes.asset]); final newAssetStore = AssetsStore(); for (var order in orders) { final productId = order.productId; Log.v("init order:${order.sku} $productId"); newAssetStore.addAsset(Asset(productId, order)); } _assetStoreSubject.addEx(newAssetStore); } Future clearAssets({String? category, TransactionMethod? method}) async { await GuruDB.instance.clearOrders(category: category, method: method); final newAssetStore = purchasedStore.clone()..clearAsset(category: category, method: method); _assetStoreSubject.addEx(newAssetStore); } Future accumulate(int igc, TransactionMethod method, {String? scene}) async { try { int newBalance = await AppProperty.getInstance().accumulateIgc(igc); try { if (method == TransactionMethod.iap) { final iapIgc = await AppProperty.getInstance().accumulateIapIgc(igc); await GuruAnalytics.instance.setUserProperty("iap_coin", iapIgc.toString()); } else { final noniapIgc = await AppProperty.getInstance().accumulateNoIapIgc(igc); await GuruAnalytics.instance.setUserProperty("noniap_coin", noniapIgc.toString()); } await GuruAnalytics.instance.setUserProperty("coin", newBalance.toString()); } catch (throwable, stacktrace) { Log.w("accumulate error $throwable", syncFirebase: true, syncCrashlytics: true); } _balanceSubject.add(newBalance); GuruAnalytics.instance.logEarnVirtualCurrency( virtualCurrencyName: "coin", method: scene ?? convertTransactionMethodName(method), balance: newBalance, value: igc); return true; } catch (error, stacktrace) { Log.v("accumulate error:$error $stacktrace"); } return false; } Future clear() async { final result = await AppProperty.getInstance().clearAllIgc(); if (result) { _balanceSubject.add(0); } } Future buildIgcProduct(TransactionIntent intent) async { final manifest = await ManifestManager.instance.createManifest(intent); return IgcProduct(intent.productId, manifest, intent.igcCost); } Future purchase(IgcProduct product) async { Log.v("Igc buy"); final purchasedItem = purchasedStore.getAsset(product.productId); if (purchasedItem != null) { Log.v("Coin buy ${purchasedItem.productId} direct success!"); return true; } return _requestPurchase(product); } Future _requestPurchase(IgcProduct product) async { if (currentBalance < product.cost || product.cost < 0) { Log.v("_requestPurchase error! $currentBalance price:${product.cost}"); return false; } try { final int newBalance = await AppProperty.getInstance().consumeIgc(product.cost); await GuruAnalytics.instance.setUserProperty("coin", newBalance.toString()); if (currentBalance != newBalance) { _balanceSubject.add(newBalance); } if (product.cost != 0) { GuruAnalytics.instance.logSpendCredits( product.productId.sku, product.manifest.category, product.cost, virtualCurrencyName: "coin", balance: newBalance, scene: product.manifest.scene); } if (!product.productId.isConsumable) { final order = product.createOrder(); await GuruDB.instance.upsertOrder(order: order).catchError((error) { Log.v("upsertOrder error!$error"); return false; }); final newPurchasedStore = purchasedStore.clone(); newPurchasedStore.addAsset(Asset(product.productId, order)); _assetStoreSubject.addEx(newPurchasedStore); } return true; } catch (error, stacktrace) { Log.v("error $error, $stacktrace"); return false; } } void dispose() { // _productStoreSubject.close(); _assetStoreSubject.close(); _balanceSubject.close(); } }