some refactoring to subscriptions, added auto 1-day pretrial
This commit is contained in:
parent
c8865b12a4
commit
d0caf01e4d
15 changed files with 251 additions and 388 deletions
|
|
@ -54,8 +54,9 @@ class SettingsSecurityController extends State<SettingsSecurity> {
|
|||
// #Pangea
|
||||
final subscriptionController =
|
||||
MatrixState.pangeaController.subscriptionController;
|
||||
if (subscriptionController.subscription?.isPaidSubscription == true &&
|
||||
subscriptionController.subscription?.defaultManagementURL != null) {
|
||||
if (subscriptionController.currentSubscriptionInfo?.isPaidSubscription ==
|
||||
true &&
|
||||
subscriptionController.defaultManagementURL != null) {
|
||||
final resp = await showOkCancelAlertDialog(
|
||||
useRootNavigator: false,
|
||||
context: context,
|
||||
|
|
@ -66,7 +67,7 @@ class SettingsSecurityController extends State<SettingsSecurity> {
|
|||
);
|
||||
if (resp == OkCancelResult.ok) {
|
||||
launchUrlString(
|
||||
subscriptionController.subscription!.defaultManagementURL!,
|
||||
subscriptionController.defaultManagementURL!,
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/constants/local.key.dart';
|
||||
import 'package:fluffychat/pangea/controllers/base_controller.dart';
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/controllers/user_controller.dart';
|
||||
import 'package:fluffychat/pangea/models/base_subscription_info.dart';
|
||||
import 'package:fluffychat/pangea/models/mobile_subscriptions.dart';
|
||||
import 'package:fluffychat/pangea/models/web_subscriptions.dart';
|
||||
|
|
@ -13,6 +14,7 @@ import 'package:fluffychat/pangea/network/requests.dart';
|
|||
import 'package:fluffychat/pangea/network/urls.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/utils/firebase_analytics.dart';
|
||||
import 'package:fluffychat/pangea/utils/subscription_app_id.dart';
|
||||
import 'package:fluffychat/pangea/widgets/subscription/subscription_paywall.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
|
@ -31,7 +33,10 @@ enum SubscriptionStatus {
|
|||
|
||||
class SubscriptionController extends BaseController {
|
||||
late PangeaController _pangeaController;
|
||||
SubscriptionInfo? subscription;
|
||||
|
||||
CurrentSubscriptionInfo? currentSubscriptionInfo;
|
||||
AvailableSubscriptionsInfo? availableSubscriptionInfo;
|
||||
|
||||
final StreamController subscriptionStream = StreamController.broadcast();
|
||||
final StreamController trialActivationStream = StreamController.broadcast();
|
||||
|
||||
|
|
@ -39,10 +44,11 @@ class SubscriptionController extends BaseController {
|
|||
_pangeaController = pangeaController;
|
||||
}
|
||||
|
||||
UserController get userController => _pangeaController.userController;
|
||||
String? get userID => _pangeaController.matrixState.client.userID;
|
||||
|
||||
bool get isSubscribed =>
|
||||
subscription != null &&
|
||||
(subscription!.currentSubscriptionId != null ||
|
||||
subscription!.currentSubscription != null);
|
||||
currentSubscriptionInfo?.currentSubscriptionId != null;
|
||||
|
||||
bool _isInitializing = false;
|
||||
Completer<void> initialized = Completer<void>();
|
||||
|
|
@ -67,18 +73,27 @@ class SubscriptionController extends BaseController {
|
|||
|
||||
Future<void> _initialize() async {
|
||||
try {
|
||||
if (_pangeaController.matrixState.client.userID == null) {
|
||||
if (userID == null) {
|
||||
debugPrint(
|
||||
"Attempted to initalize subscription information with null userId",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
subscription = kIsWeb
|
||||
? WebSubscriptionInfo(pangeaController: _pangeaController)
|
||||
: MobileSubscriptionInfo(pangeaController: _pangeaController);
|
||||
availableSubscriptionInfo = AvailableSubscriptionsInfo();
|
||||
await availableSubscriptionInfo!.setAvailableSubscriptions();
|
||||
|
||||
await subscription!.configure();
|
||||
currentSubscriptionInfo = kIsWeb
|
||||
? WebSubscriptionInfo(
|
||||
userID: userID!,
|
||||
availableSubscriptionInfo: availableSubscriptionInfo!,
|
||||
)
|
||||
: MobileSubscriptionInfo(
|
||||
userID: userID!,
|
||||
availableSubscriptionInfo: availableSubscriptionInfo!,
|
||||
);
|
||||
|
||||
await currentSubscriptionInfo!.configure();
|
||||
if (_activatedNewUserTrial) {
|
||||
setNewUserTrial();
|
||||
}
|
||||
|
|
@ -101,7 +116,7 @@ class SubscriptionController extends BaseController {
|
|||
await _pangeaController.pStoreService.delete(
|
||||
PLocalKey.beganWebPayment,
|
||||
);
|
||||
if (_pangeaController.subscriptionController.isSubscribed) {
|
||||
if (isSubscribed) {
|
||||
subscriptionStream.add(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -170,7 +185,7 @@ class SubscriptionController extends BaseController {
|
|||
return;
|
||||
}
|
||||
ErrorHandler.logError(
|
||||
m: "Failed to purchase revenuecat package for user ${_pangeaController.matrixState.client.userID} with error code $errCode",
|
||||
m: "Failed to purchase revenuecat package for user $userID with error code $errCode",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
return;
|
||||
|
|
@ -178,14 +193,19 @@ class SubscriptionController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
bool get _activatedNewUserTrial {
|
||||
final bool activated = _pangeaController
|
||||
.userController.profile.userSettings.activatedFreeTrial;
|
||||
return _pangeaController.userController.inTrialWindow && activated;
|
||||
}
|
||||
int get currentTrialDays => userController.inTrialWindow(trialDays: 1)
|
||||
? 1
|
||||
: userController.inTrialWindow(trialDays: 7)
|
||||
? 7
|
||||
: 0;
|
||||
|
||||
bool get _activatedNewUserTrial =>
|
||||
userController.inTrialWindow(trialDays: 1) ||
|
||||
(userController.inTrialWindow() &&
|
||||
userController.profile.userSettings.activatedFreeTrial);
|
||||
|
||||
void activateNewUserTrial() {
|
||||
_pangeaController.userController.updateProfile(
|
||||
userController.updateProfile(
|
||||
(profile) {
|
||||
profile.userSettings.activatedFreeTrial = true;
|
||||
return profile;
|
||||
|
|
@ -196,8 +216,7 @@ class SubscriptionController extends BaseController {
|
|||
}
|
||||
|
||||
void setNewUserTrial() {
|
||||
final DateTime? createdAt =
|
||||
_pangeaController.userController.profile.userSettings.createdAt;
|
||||
final DateTime? createdAt = userController.profile.userSettings.createdAt;
|
||||
if (createdAt == null) {
|
||||
ErrorHandler.logError(
|
||||
m: "Null user profile createAt in subscription settings",
|
||||
|
|
@ -207,23 +226,16 @@ class SubscriptionController extends BaseController {
|
|||
}
|
||||
|
||||
final DateTime expirationDate = createdAt.add(
|
||||
const Duration(days: 7),
|
||||
Duration(days: currentTrialDays),
|
||||
);
|
||||
subscription?.setTrial(expirationDate);
|
||||
currentSubscriptionInfo?.setTrial(expirationDate);
|
||||
}
|
||||
|
||||
Future<void> updateCustomerInfo() async {
|
||||
if (!initialized.isCompleted) {
|
||||
await initialize();
|
||||
}
|
||||
if (subscription == null) {
|
||||
ErrorHandler.logError(
|
||||
m: "Null subscription info in subscription settings",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
return;
|
||||
}
|
||||
await subscription!.setCustomerInfo();
|
||||
await currentSubscriptionInfo!.setCurrentSubscription();
|
||||
setState(null);
|
||||
}
|
||||
|
||||
|
|
@ -284,7 +296,7 @@ class SubscriptionController extends BaseController {
|
|||
if (!initialized.isCompleted) {
|
||||
await initialize();
|
||||
}
|
||||
if (subscription?.availableSubscriptions.isEmpty ?? true) {
|
||||
if (availableSubscriptionInfo?.availableSubscriptions.isEmpty ?? true) {
|
||||
return;
|
||||
}
|
||||
if (isSubscribed) return;
|
||||
|
|
@ -310,70 +322,51 @@ class SubscriptionController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
Future<String> getPaymentLink(String duration, {bool isPromo = false}) async {
|
||||
Future<String> getPaymentLink(
|
||||
SubscriptionDuration duration, {
|
||||
bool isPromo = false,
|
||||
}) async {
|
||||
final Requests req = Requests(baseUrl: PApiUrls.baseAPI);
|
||||
final String reqUrl = Uri.encodeFull(
|
||||
"${PApiUrls.paymentLink}?pangea_user_id=${_pangeaController.matrixState.client.userID}&duration=$duration&redeem=$isPromo",
|
||||
"${PApiUrls.paymentLink}?pangea_user_id=$userID&duration=${duration.value}&redeem=$isPromo",
|
||||
);
|
||||
final Response res = await req.get(url: reqUrl);
|
||||
final json = jsonDecode(res.body);
|
||||
String paymentLink = json["link"]["url"];
|
||||
|
||||
final String? email = await _pangeaController.userController.userEmail;
|
||||
final String? email = await userController.userEmail;
|
||||
if (email != null) {
|
||||
paymentLink += "?prefilled_email=${Uri.encodeComponent(email)}";
|
||||
}
|
||||
return paymentLink;
|
||||
}
|
||||
|
||||
Future<bool> fetchSubscriptionStatus() async {
|
||||
final Requests req = Requests(baseUrl: PApiUrls.baseAPI);
|
||||
final String reqUrl = Uri.encodeFull(
|
||||
"${PApiUrls.subscriptionExpiration}?pangea_user_id=${_pangeaController.matrixState.client.userID}",
|
||||
);
|
||||
String? get defaultManagementURL =>
|
||||
currentSubscriptionInfo?.currentSubscription
|
||||
?.defaultManagementURL(availableSubscriptionInfo?.appIds);
|
||||
}
|
||||
|
||||
DateTime? expiration;
|
||||
try {
|
||||
final Response res = await req.get(url: reqUrl);
|
||||
final json = jsonDecode(res.body);
|
||||
if (json["premium_expires_date"] != null) {
|
||||
expiration = DateTime.parse(json["premium_expires_date"]);
|
||||
}
|
||||
} catch (err) {
|
||||
ErrorHandler.logError(
|
||||
e: "Failed to fetch subscripton status for user ${_pangeaController.matrixState.client.userID}",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
}
|
||||
final bool subscribed =
|
||||
expiration == null ? false : DateTime.now().isBefore(expiration);
|
||||
GoogleAnalytics.updateUserSubscriptionStatus(subscribed);
|
||||
return subscribed;
|
||||
}
|
||||
enum SubscriptionPeriodType {
|
||||
normal,
|
||||
trial,
|
||||
}
|
||||
|
||||
Future<void> redeemPromoCode(BuildContext context) async {
|
||||
final List<String>? promoCode = await showTextInputDialog(
|
||||
useRootNavigator: false,
|
||||
context: context,
|
||||
title: L10n.of(context)!.enterPromoCode,
|
||||
okLabel: L10n.of(context)!.ok,
|
||||
cancelLabel: L10n.of(context)!.cancel,
|
||||
textFields: [const DialogTextField()],
|
||||
);
|
||||
if (promoCode == null || promoCode.single.isEmpty) return;
|
||||
launchUrlString(
|
||||
"${AppConfig.iosPromoCode}${promoCode.single}",
|
||||
);
|
||||
}
|
||||
enum SubscriptionDuration {
|
||||
month,
|
||||
year,
|
||||
}
|
||||
|
||||
extension SubscriptionDurationExtension on SubscriptionDuration {
|
||||
String get value => this == SubscriptionDuration.month ? "month" : "year";
|
||||
}
|
||||
|
||||
class SubscriptionDetails {
|
||||
double price;
|
||||
String? duration;
|
||||
Package? package;
|
||||
String? appId;
|
||||
final double price;
|
||||
final SubscriptionDuration? duration;
|
||||
final String? appId;
|
||||
final String id;
|
||||
String? periodType = "normal";
|
||||
SubscriptionPeriodType periodType;
|
||||
Package? package;
|
||||
|
||||
SubscriptionDetails({
|
||||
required this.price,
|
||||
|
|
@ -381,30 +374,35 @@ class SubscriptionDetails {
|
|||
this.duration,
|
||||
this.package,
|
||||
this.appId,
|
||||
this.periodType,
|
||||
this.periodType = SubscriptionPeriodType.normal,
|
||||
});
|
||||
|
||||
void makeTrial() => periodType = 'trial';
|
||||
bool get isTrial => periodType == 'trial';
|
||||
void makeTrial() => periodType = SubscriptionPeriodType.trial;
|
||||
bool get isTrial => periodType == SubscriptionPeriodType.trial;
|
||||
|
||||
String displayPrice(BuildContext context) {
|
||||
if (isTrial || price <= 0) {
|
||||
return L10n.of(context)!.freeTrial;
|
||||
}
|
||||
return "\$${price.toStringAsFixed(2)}";
|
||||
}
|
||||
String displayPrice(BuildContext context) => isTrial || price <= 0
|
||||
? L10n.of(context)!.freeTrial
|
||||
: "\$${price.toStringAsFixed(2)}";
|
||||
|
||||
String displayName(BuildContext context) {
|
||||
if (isTrial) {
|
||||
return L10n.of(context)!.oneWeekTrial;
|
||||
}
|
||||
switch (duration) {
|
||||
case ('month'):
|
||||
case (SubscriptionDuration.month):
|
||||
return L10n.of(context)!.monthlySubscription;
|
||||
case ('year'):
|
||||
case (SubscriptionDuration.year):
|
||||
return L10n.of(context)!.yearlySubscription;
|
||||
default:
|
||||
return L10n.of(context)!.defaultSubscription;
|
||||
}
|
||||
}
|
||||
|
||||
String? defaultManagementURL(SubscriptionAppIds? appIds) {
|
||||
return appId == appIds?.androidId
|
||||
? AppConfig.googlePlayMangementUrl
|
||||
: appId == appIds?.appleId
|
||||
? AppConfig.appleMangementUrl
|
||||
: Environment.stripeManagementUrl;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -196,13 +196,13 @@ class UserController extends BaseController {
|
|||
}
|
||||
|
||||
/// Returns a boolean value indicating whether the user is currently in the trial window.
|
||||
bool get inTrialWindow {
|
||||
bool inTrialWindow({int trialDays = 7}) {
|
||||
final DateTime? createdAt = profile.userSettings.createdAt;
|
||||
if (createdAt == null) {
|
||||
return false;
|
||||
}
|
||||
return createdAt.isAfter(
|
||||
DateTime.now().subtract(const Duration(days: 7)),
|
||||
DateTime.now().subtract(Duration(days: trialDays)),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,51 +1,33 @@
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/controllers/subscription_controller.dart';
|
||||
import 'package:fluffychat/pangea/repo/subscription_repo.dart';
|
||||
import 'package:fluffychat/pangea/utils/subscription_app_id.dart';
|
||||
|
||||
class SubscriptionInfo {
|
||||
PangeaController pangeaController;
|
||||
List<SubscriptionDetails> availableSubscriptions = [];
|
||||
String? currentSubscriptionId;
|
||||
SubscriptionDetails? currentSubscription;
|
||||
// Gabby - is it necessary to store appIds for each platform?
|
||||
SubscriptionAppIds? appIds;
|
||||
List<SubscriptionDetails>? allProducts;
|
||||
final SubscriptionPlatform platform = SubscriptionPlatform();
|
||||
List<String> allEntitlements = [];
|
||||
/// Contains information about the users's current subscription
|
||||
class CurrentSubscriptionInfo {
|
||||
final String userID;
|
||||
final AvailableSubscriptionsInfo availableSubscriptionInfo;
|
||||
|
||||
DateTime? expirationDate;
|
||||
String? currentSubscriptionId;
|
||||
|
||||
bool get hasSubscribed => allEntitlements.isNotEmpty;
|
||||
CurrentSubscriptionInfo({
|
||||
required this.userID,
|
||||
required this.availableSubscriptionInfo,
|
||||
});
|
||||
|
||||
SubscriptionInfo({
|
||||
required this.pangeaController,
|
||||
}) : super();
|
||||
SubscriptionDetails? get currentSubscription {
|
||||
if (currentSubscriptionId == null) return null;
|
||||
return availableSubscriptionInfo.allProducts?.firstWhereOrNull(
|
||||
(SubscriptionDetails sub) =>
|
||||
sub.id.contains(currentSubscriptionId!) ||
|
||||
currentSubscriptionId!.contains(sub.id),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> configure() async {}
|
||||
|
||||
//TO-DO - hey Gabby this file feels like it could be reorganized. i'd like to
|
||||
// 1) move these api calls to a class in a file in repo and
|
||||
// 2) move the url to the urls file.
|
||||
// 3) any stateful info to the subscription controller
|
||||
// let's discuss before you make the changes though
|
||||
// maybe you had some reason for this organization
|
||||
|
||||
/*
|
||||
Fetch App Ids for each RC app (iOS, Android, and Stripe). Used to determine which app a user
|
||||
with an active subscription purchased that subscription.
|
||||
*/
|
||||
Future<void> setAppIds() async {
|
||||
if (appIds != null) return;
|
||||
appIds = await SubscriptionRepo.getAppIds();
|
||||
}
|
||||
|
||||
Future<void> setAllProducts() async {
|
||||
if (allProducts != null) return;
|
||||
allProducts = await SubscriptionRepo.getAllProducts();
|
||||
}
|
||||
|
||||
bool get isNewUserTrial =>
|
||||
currentSubscriptionId == AppConfig.trialSubscriptionId;
|
||||
|
||||
|
|
@ -64,41 +46,69 @@ class SubscriptionInfo {
|
|||
|
||||
String? get purchasePlatformDisplayName {
|
||||
if (currentSubscription?.appId == null) return null;
|
||||
return appIds?.appDisplayName(currentSubscription!.appId!);
|
||||
return availableSubscriptionInfo.appIds
|
||||
?.appDisplayName(currentSubscription!.appId!);
|
||||
}
|
||||
|
||||
bool get purchasedOnWeb =>
|
||||
(currentSubscription != null && appIds != null) &&
|
||||
(currentSubscription?.appId == appIds?.stripeId);
|
||||
(currentSubscription != null &&
|
||||
availableSubscriptionInfo.appIds != null) &&
|
||||
(currentSubscription?.appId ==
|
||||
availableSubscriptionInfo.appIds?.stripeId);
|
||||
|
||||
bool get currentPlatformMatchesPurchasePlatform =>
|
||||
(currentSubscription != null && appIds != null) &&
|
||||
(currentSubscription?.appId == appIds?.currentAppId);
|
||||
(currentSubscription != null &&
|
||||
availableSubscriptionInfo.appIds != null) &&
|
||||
(currentSubscription?.appId ==
|
||||
availableSubscriptionInfo.appIds?.currentAppId);
|
||||
|
||||
void resetSubscription() {
|
||||
currentSubscription = null;
|
||||
currentSubscriptionId = null;
|
||||
}
|
||||
void resetSubscription() => currentSubscriptionId = null;
|
||||
|
||||
void setTrial(DateTime expiration) {
|
||||
if (currentSubscription != null) return;
|
||||
expirationDate = expiration;
|
||||
currentSubscriptionId = AppConfig.trialSubscriptionId;
|
||||
currentSubscription = SubscriptionDetails(
|
||||
price: 0,
|
||||
id: AppConfig.trialSubscriptionId,
|
||||
periodType: 'trial',
|
||||
);
|
||||
if (currentSubscription == null) {
|
||||
availableSubscriptionInfo.availableSubscriptions.add(
|
||||
SubscriptionDetails(
|
||||
price: 0,
|
||||
id: AppConfig.trialSubscriptionId,
|
||||
periodType: SubscriptionPeriodType.trial,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setCustomerInfo() async {}
|
||||
Future<void> setCurrentSubscription() async {}
|
||||
}
|
||||
|
||||
String? get defaultManagementURL {
|
||||
final String? purchaseAppId = currentSubscription?.appId;
|
||||
return purchaseAppId == appIds?.androidId
|
||||
? AppConfig.googlePlayMangementUrl
|
||||
: purchaseAppId == appIds?.appleId
|
||||
? AppConfig.appleMangementUrl
|
||||
: Environment.stripeManagementUrl;
|
||||
/// Contains information about the suscriptions available on revenuecat
|
||||
class AvailableSubscriptionsInfo {
|
||||
List<SubscriptionDetails> availableSubscriptions = [];
|
||||
SubscriptionAppIds? appIds;
|
||||
List<SubscriptionDetails>? allProducts;
|
||||
|
||||
Future<void> setAvailableSubscriptions() async {
|
||||
appIds ??= await SubscriptionRepo.getAppIds();
|
||||
allProducts ??= await SubscriptionRepo.getAllProducts();
|
||||
availableSubscriptions = (allProducts ?? [])
|
||||
.where((product) => product.appId == appIds!.currentAppId)
|
||||
.sorted((a, b) => a.price.compareTo(b.price))
|
||||
.toList();
|
||||
// //@Gabby - temporary solution to add trial to list
|
||||
// if (currentSubscriptionId == null && !hasSubscribed) {
|
||||
// final id = availableSubscriptions[0].id;
|
||||
// final package = availableSubscriptions[0].package;
|
||||
// final duration = availableSubscriptions[0].duration;
|
||||
// availableSubscriptions.insert(
|
||||
// 0,
|
||||
// SubscriptionDetails(
|
||||
// price: 0,
|
||||
// id: id,
|
||||
// duration: duration,
|
||||
// package: package,
|
||||
// periodType: SubscriptionPeriodType.trial,
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fluffychat/pangea/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/controllers/subscription_controller.dart';
|
||||
import 'package:fluffychat/pangea/models/base_subscription_info.dart';
|
||||
|
|
@ -9,8 +8,11 @@ import 'package:flutter/material.dart';
|
|||
import 'package:purchases_flutter/purchases_flutter.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
class MobileSubscriptionInfo extends SubscriptionInfo {
|
||||
MobileSubscriptionInfo({required super.pangeaController}) : super();
|
||||
class MobileSubscriptionInfo extends CurrentSubscriptionInfo {
|
||||
MobileSubscriptionInfo({
|
||||
required super.userID,
|
||||
required super.availableSubscriptionInfo,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<void> configure() async {
|
||||
|
|
@ -19,112 +21,42 @@ class MobileSubscriptionInfo extends SubscriptionInfo {
|
|||
: PurchasesConfiguration(Environment.rcIosKey);
|
||||
try {
|
||||
await Purchases.configure(
|
||||
configuration..appUserID = pangeaController.userController.userId,
|
||||
configuration..appUserID = userID,
|
||||
);
|
||||
await super.configure();
|
||||
await setMobilePackages();
|
||||
} catch (err) {
|
||||
ErrorHandler.logError(
|
||||
m: "Failed to configure revenuecat SDK for user ${pangeaController.userController.userId}",
|
||||
m: "Failed to configure revenuecat SDK",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
debugPrint(
|
||||
"Failed to configure revenuecat SDK for user ${pangeaController.userController.userId}",
|
||||
);
|
||||
return;
|
||||
}
|
||||
await setAppIds();
|
||||
await setAllProducts();
|
||||
await setCustomerInfo();
|
||||
await setMobilePackages();
|
||||
if (allProducts != null && appIds != null) {
|
||||
availableSubscriptions = allProducts!
|
||||
.where((product) => product.appId == appIds!.currentAppId)
|
||||
.toList();
|
||||
availableSubscriptions.sort((a, b) => a.price.compareTo(b.price));
|
||||
|
||||
if (currentSubscriptionId == null && !hasSubscribed) {
|
||||
//@Gabby - temporary solution to add trial to list
|
||||
final id = availableSubscriptions[0].id;
|
||||
final package = availableSubscriptions[0].package;
|
||||
final duration = availableSubscriptions[0].duration;
|
||||
availableSubscriptions.insert(
|
||||
0,
|
||||
SubscriptionDetails(
|
||||
price: 0,
|
||||
id: id,
|
||||
duration: duration,
|
||||
package: package,
|
||||
periodType: 'trial',
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
ErrorHandler.logError(e: Exception("allProducts null || appIds null"));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setMobilePackages() async {
|
||||
if (allProducts == null) {
|
||||
ErrorHandler.logError(
|
||||
m: "Null appProducts in setMobilePrices",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
debugPrint(
|
||||
"Null appProducts in setMobilePrices",
|
||||
);
|
||||
return;
|
||||
}
|
||||
Offerings offerings;
|
||||
try {
|
||||
offerings = await Purchases.getOfferings();
|
||||
} catch (err) {
|
||||
ErrorHandler.logError(
|
||||
m: "Failed to fetch revenuecat offerings from revenuecat",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
debugPrint(
|
||||
"Failed to fetch revenuecat offerings from revenuecat",
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (availableSubscriptionInfo.allProducts == null) return;
|
||||
|
||||
final Offerings offerings = await Purchases.getOfferings();
|
||||
final Offering? offering = offerings.all[Environment.rcOfferingName];
|
||||
if (offering != null) {
|
||||
final List<SubscriptionDetails> mobileSubscriptions =
|
||||
offering.availablePackages
|
||||
.map(
|
||||
(package) {
|
||||
return SubscriptionDetails(
|
||||
price: package.storeProduct.price,
|
||||
id: package.storeProduct.identifier,
|
||||
package: package,
|
||||
);
|
||||
},
|
||||
)
|
||||
.toList()
|
||||
.cast<SubscriptionDetails>();
|
||||
for (final SubscriptionDetails mobileSub in mobileSubscriptions) {
|
||||
final int productIndex = allProducts!
|
||||
.indexWhere((product) => product.id.contains(mobileSub.id));
|
||||
if (productIndex >= 0) {
|
||||
final SubscriptionDetails updated = allProducts![productIndex];
|
||||
updated.package = mobileSub.package;
|
||||
allProducts![productIndex] = updated;
|
||||
}
|
||||
}
|
||||
if (offering == null) return;
|
||||
|
||||
final products = availableSubscriptionInfo.allProducts;
|
||||
for (final package in offering.availablePackages) {
|
||||
final int productIndex = products!.indexWhere(
|
||||
(product) => product.id.contains(package.storeProduct.identifier),
|
||||
);
|
||||
|
||||
if (productIndex < 0) continue;
|
||||
final SubscriptionDetails updated =
|
||||
availableSubscriptionInfo.allProducts![productIndex];
|
||||
updated.package = package;
|
||||
availableSubscriptionInfo.allProducts![productIndex] = updated;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setCustomerInfo() async {
|
||||
if (allProducts == null) {
|
||||
ErrorHandler.logError(
|
||||
m: "Null allProducts in setCustomerInfo",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
debugPrint(
|
||||
"Null allProducts in setCustomerInfo",
|
||||
);
|
||||
return;
|
||||
}
|
||||
Future<void> setCurrentSubscription() async {
|
||||
if (availableSubscriptionInfo.allProducts == null) return;
|
||||
|
||||
CustomerInfo info;
|
||||
try {
|
||||
|
|
@ -132,28 +64,11 @@ class MobileSubscriptionInfo extends SubscriptionInfo {
|
|||
info = await Purchases.getCustomerInfo();
|
||||
} catch (err) {
|
||||
ErrorHandler.logError(
|
||||
m: "Failed to fetch revenuecat customer info for user ${pangeaController.userController.userId}",
|
||||
m: "Failed to fetch revenuecat customer info",
|
||||
s: StackTrace.current,
|
||||
);
|
||||
debugPrint(
|
||||
"Failed to fetch revenuecat customer info for user ${pangeaController.userController.userId}",
|
||||
);
|
||||
return;
|
||||
}
|
||||
final List<EntitlementInfo> noExpirations =
|
||||
getEntitlementsWithoutExpiration(info);
|
||||
|
||||
if (noExpirations.isNotEmpty) {
|
||||
Sentry.addBreadcrumb(
|
||||
Breadcrumb(
|
||||
message:
|
||||
"Found revenuecat entitlement(s) without expiration date for user ${pangeaController.userController.userId}: ${noExpirations.map(
|
||||
(entry) =>
|
||||
"Entitlement Id: ${entry.identifier}, Purchase Date: ${entry.originalPurchaseDate}",
|
||||
)}",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final List<EntitlementInfo> activeEntitlements =
|
||||
info.entitlements.all.entries
|
||||
|
|
@ -166,14 +81,6 @@ class MobileSubscriptionInfo extends SubscriptionInfo {
|
|||
.map((MapEntry<String, EntitlementInfo> entry) => entry.value)
|
||||
.toList();
|
||||
|
||||
allEntitlements = info.entitlements.all.entries
|
||||
.map(
|
||||
(MapEntry<String, EntitlementInfo> entry) =>
|
||||
entry.value.productIdentifier,
|
||||
)
|
||||
.cast<String>()
|
||||
.toList();
|
||||
|
||||
if (activeEntitlements.length > 1) {
|
||||
debugPrint(
|
||||
"User has more than one active entitlement.",
|
||||
|
|
@ -185,13 +92,9 @@ class MobileSubscriptionInfo extends SubscriptionInfo {
|
|||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final EntitlementInfo activeEntitlement = activeEntitlements[0];
|
||||
currentSubscriptionId = activeEntitlement.productIdentifier;
|
||||
currentSubscription = allProducts!.firstWhereOrNull(
|
||||
(SubscriptionDetails sub) =>
|
||||
sub.id.contains(currentSubscriptionId!) ||
|
||||
currentSubscriptionId!.contains(sub.id),
|
||||
);
|
||||
expirationDate = activeEntitlement.expirationDate != null
|
||||
? DateTime.parse(activeEntitlement.expirationDate!)
|
||||
: null;
|
||||
|
|
@ -205,15 +108,4 @@ class MobileSubscriptionInfo extends SubscriptionInfo {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
List<EntitlementInfo> getEntitlementsWithoutExpiration(CustomerInfo info) {
|
||||
final List<EntitlementInfo> noExpirations = info.entitlements.all.entries
|
||||
.where(
|
||||
(MapEntry<String, EntitlementInfo> entry) =>
|
||||
entry.value.expirationDate == null,
|
||||
)
|
||||
.map((MapEntry<String, EntitlementInfo> entry) => entry.value)
|
||||
.toList();
|
||||
return noExpirations;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,61 +1,23 @@
|
|||
import 'package:fluffychat/pangea/controllers/subscription_controller.dart';
|
||||
import 'package:fluffychat/pangea/models/base_subscription_info.dart';
|
||||
import 'package:fluffychat/pangea/repo/subscription_repo.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
class WebSubscriptionInfo extends SubscriptionInfo {
|
||||
WebSubscriptionInfo({required super.pangeaController}) : super();
|
||||
class WebSubscriptionInfo extends CurrentSubscriptionInfo {
|
||||
WebSubscriptionInfo({
|
||||
required super.userID,
|
||||
required super.availableSubscriptionInfo,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<void> configure() async {
|
||||
await setAppIds();
|
||||
await setAllProducts();
|
||||
await setCustomerInfo();
|
||||
|
||||
if (allProducts == null || appIds == null) {
|
||||
Sentry.addBreadcrumb(
|
||||
Breadcrumb(message: "No products found for current app"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
availableSubscriptions = allProducts!
|
||||
.where((product) => product.appId == appIds!.currentAppId)
|
||||
.toList();
|
||||
availableSubscriptions.sort((a, b) => a.price.compareTo(b.price));
|
||||
//@Gabby - temporary solution to add trial to list
|
||||
if (currentSubscriptionId == null && !hasSubscribed) {
|
||||
final id = availableSubscriptions[0].id;
|
||||
final package = availableSubscriptions[0].package;
|
||||
final duration = availableSubscriptions[0].duration;
|
||||
availableSubscriptions.insert(
|
||||
0,
|
||||
SubscriptionDetails(
|
||||
price: 0,
|
||||
id: id,
|
||||
duration: duration,
|
||||
package: package,
|
||||
periodType: 'trial',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setCustomerInfo() async {
|
||||
if (currentSubscriptionId != null && currentSubscription != null) {
|
||||
return;
|
||||
}
|
||||
final RCSubscriptionResponseModel currentSubscriptionInfo =
|
||||
await SubscriptionRepo.getCurrentSubscriptionInfo(
|
||||
pangeaController.matrixState.client.userID,
|
||||
allProducts,
|
||||
Future<void> setCurrentSubscription() async {
|
||||
if (currentSubscriptionId != null) return;
|
||||
final rcResponse = await SubscriptionRepo.getCurrentSubscriptionInfo(
|
||||
userID,
|
||||
availableSubscriptionInfo.allProducts,
|
||||
);
|
||||
|
||||
currentSubscriptionId = currentSubscriptionInfo.currentSubscriptionId;
|
||||
currentSubscription = currentSubscriptionInfo.currentSubscription;
|
||||
allEntitlements = currentSubscriptionInfo.allEntitlements ?? [];
|
||||
expirationDate = currentSubscriptionInfo.expirationDate;
|
||||
currentSubscriptionId = rcResponse.currentSubscriptionId;
|
||||
expirationDate = rcResponse.expirationDate;
|
||||
|
||||
if (currentSubscriptionId != null && currentSubscription == null) {
|
||||
Sentry.addBreadcrumb(
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ class PUserAgeController extends State<PUserAge> {
|
|||
return profile;
|
||||
});
|
||||
}
|
||||
pangeaController.subscriptionController.reinitialize();
|
||||
FluffyChatApp.router.go('/rooms');
|
||||
} catch (err, s) {
|
||||
setState(() {
|
||||
|
|
|
|||
|
|
@ -57,30 +57,33 @@ class SubscriptionManagementController extends State<SubscriptionManagement> {
|
|||
}
|
||||
|
||||
bool get subscriptionsAvailable =>
|
||||
subscriptionController.subscription?.availableSubscriptions.isNotEmpty ??
|
||||
subscriptionController
|
||||
.availableSubscriptionInfo?.availableSubscriptions.isNotEmpty ??
|
||||
false;
|
||||
|
||||
bool get currentSubscriptionAvailable =>
|
||||
subscriptionController.isSubscribed &&
|
||||
subscriptionController.subscription?.currentSubscription != null;
|
||||
subscriptionController.currentSubscriptionInfo?.currentSubscription !=
|
||||
null;
|
||||
|
||||
String? get purchasePlatformDisplayName =>
|
||||
subscriptionController.subscription?.purchasePlatformDisplayName;
|
||||
String? get purchasePlatformDisplayName => subscriptionController
|
||||
.currentSubscriptionInfo?.purchasePlatformDisplayName;
|
||||
|
||||
bool get currentSubscriptionIsPromotional =>
|
||||
subscriptionController.subscription?.currentSubscriptionIsPromotional ??
|
||||
subscriptionController
|
||||
.currentSubscriptionInfo?.currentSubscriptionIsPromotional ??
|
||||
false;
|
||||
|
||||
bool get isNewUserTrial =>
|
||||
subscriptionController.subscription?.isNewUserTrial ?? false;
|
||||
subscriptionController.currentSubscriptionInfo?.isNewUserTrial ?? false;
|
||||
|
||||
String get currentSubscriptionTitle =>
|
||||
subscriptionController.subscription?.currentSubscription
|
||||
subscriptionController.currentSubscriptionInfo?.currentSubscription
|
||||
?.displayName(context) ??
|
||||
"";
|
||||
|
||||
String get currentSubscriptionPrice =>
|
||||
subscriptionController.subscription?.currentSubscription
|
||||
subscriptionController.currentSubscriptionInfo?.currentSubscription
|
||||
?.displayPrice(context) ??
|
||||
"";
|
||||
|
||||
|
|
@ -88,11 +91,11 @@ class SubscriptionManagementController extends State<SubscriptionManagement> {
|
|||
if (!currentSubscriptionAvailable || isNewUserTrial) {
|
||||
return false;
|
||||
}
|
||||
if (subscriptionController.subscription!.purchasedOnWeb) {
|
||||
if (subscriptionController.currentSubscriptionInfo!.purchasedOnWeb) {
|
||||
return true;
|
||||
}
|
||||
return subscriptionController
|
||||
.subscription!.currentPlatformMatchesPurchasePlatform;
|
||||
.currentSubscriptionInfo!.currentPlatformMatchesPurchasePlatform;
|
||||
}
|
||||
|
||||
void submitChange({bool isPromo = false}) {
|
||||
|
|
@ -122,12 +125,12 @@ class SubscriptionManagementController extends State<SubscriptionManagement> {
|
|||
if (email != null) {
|
||||
managementUrl += "?prefilled_email=${Uri.encodeComponent(email)}";
|
||||
}
|
||||
final String? purchaseAppId =
|
||||
subscriptionController.subscription?.currentSubscription?.appId;
|
||||
final String? purchaseAppId = subscriptionController
|
||||
.currentSubscriptionInfo?.currentSubscription?.appId;
|
||||
if (purchaseAppId == null) return;
|
||||
|
||||
final SubscriptionAppIds? appIds =
|
||||
subscriptionController.subscription!.appIds;
|
||||
subscriptionController.availableSubscriptionInfo!.appIds;
|
||||
|
||||
if (purchaseAppId == appIds?.stripeId) {
|
||||
launchUrlString(managementUrl);
|
||||
|
|
@ -167,7 +170,7 @@ class SubscriptionManagementController extends State<SubscriptionManagement> {
|
|||
}
|
||||
|
||||
bool isCurrentSubscription(SubscriptionDetails subscription) =>
|
||||
subscriptionController.subscription?.currentSubscription ==
|
||||
subscriptionController.currentSubscriptionInfo?.currentSubscription ==
|
||||
subscription ||
|
||||
isNewUserTrial && subscription.isTrial;
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ class SettingsSubscriptionView extends StatelessWidget {
|
|||
),
|
||||
];
|
||||
|
||||
final isSubscribed = controller.subscriptionController.isSubscribed;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
|
|
@ -63,13 +65,11 @@ class SettingsSubscriptionView extends StatelessWidget {
|
|||
child: MaxWidthBody(
|
||||
child: Column(
|
||||
children: [
|
||||
if (controller.subscriptionController.isSubscribed &&
|
||||
!controller.showManagementOptions)
|
||||
if (isSubscribed && !controller.showManagementOptions)
|
||||
ManagementNotAvailableWarning(
|
||||
controller: controller,
|
||||
),
|
||||
if (!(controller.subscriptionController.isSubscribed) ||
|
||||
controller.isNewUserTrial)
|
||||
if (!isSubscribed || controller.isNewUserTrial)
|
||||
ChangeSubscription(controller: controller),
|
||||
if (controller.showManagementOptions) ...managementButtons,
|
||||
],
|
||||
|
|
@ -90,13 +90,14 @@ class ManagementNotAvailableWarning extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final currentSubscriptionInfo =
|
||||
controller.subscriptionController.currentSubscriptionInfo;
|
||||
|
||||
String getWarningText() {
|
||||
final DateFormat formatter = DateFormat('yyyy-MM-dd');
|
||||
if (controller.isNewUserTrial) {
|
||||
return L10n.of(context)!.trialExpiration(
|
||||
formatter.format(
|
||||
controller.subscriptionController.subscription!.expirationDate!,
|
||||
),
|
||||
formatter.format(currentSubscriptionInfo!.expirationDate!),
|
||||
);
|
||||
}
|
||||
if (controller.currentSubscriptionAvailable) {
|
||||
|
|
@ -108,15 +109,11 @@ class ManagementNotAvailableWarning extends StatelessWidget {
|
|||
return warningText;
|
||||
}
|
||||
if (controller.currentSubscriptionIsPromotional) {
|
||||
if (controller
|
||||
.subscriptionController.subscription?.isLifetimeSubscription ??
|
||||
false) {
|
||||
if (currentSubscriptionInfo?.isLifetimeSubscription ?? false) {
|
||||
return L10n.of(context)!.promotionalSubscriptionDesc;
|
||||
}
|
||||
return L10n.of(context)!.promoSubscriptionExpirationDesc(
|
||||
formatter.format(
|
||||
controller.subscriptionController.subscription!.expirationDate!,
|
||||
),
|
||||
formatter.format(currentSubscriptionInfo!.expirationDate!),
|
||||
);
|
||||
}
|
||||
return L10n.of(context)!.subscriptionManagementUnavailable;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'package:fluffychat/pangea/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/controllers/subscription_controller.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/utils/subscription_app_id.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../network/urls.dart';
|
||||
|
||||
class SubscriptionRepo {
|
||||
|
|
@ -120,7 +119,9 @@ class RCProductsResponseModel {
|
|||
.map(
|
||||
(productDetails) => SubscriptionDetails(
|
||||
price: double.parse(metadata['$packageId.price']),
|
||||
duration: metadata['$packageId.duration'],
|
||||
duration: SubscriptionDuration.values.firstWhereOrNull(
|
||||
(duration) => duration.value == metadata['$packageId.duration'],
|
||||
),
|
||||
id: productDetails['product']['store_identifier'],
|
||||
appId: productDetails['product']['app_id'],
|
||||
),
|
||||
|
|
@ -150,9 +151,6 @@ class RCSubscriptionResponseModel {
|
|||
final List<String> activeEntitlements =
|
||||
RCSubscriptionResponseModel.getActiveEntitlements(json);
|
||||
|
||||
final List<String> allEntitlements =
|
||||
RCSubscriptionResponseModel.getAllEntitlements(json);
|
||||
|
||||
if (activeEntitlements.length > 1) {
|
||||
debugPrint(
|
||||
"User has more than one active entitlement. This shouldn't happen",
|
||||
|
|
|
|||
|
|
@ -49,15 +49,14 @@ enum RCPlatform {
|
|||
apple,
|
||||
}
|
||||
|
||||
class SubscriptionPlatform {
|
||||
RCPlatform currentPlatform = kIsWeb
|
||||
extension RCPlatformExtension on RCPlatform {
|
||||
RCPlatform get currentPlatform => kIsWeb
|
||||
? RCPlatform.stripe
|
||||
: Platform.isAndroid
|
||||
? RCPlatform.android
|
||||
: RCPlatform.apple;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
String get string {
|
||||
return currentPlatform == RCPlatform.stripe
|
||||
? 'stripe'
|
||||
: currentPlatform == RCPlatform.android
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class MessageUnsubscribedCard extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool inTrialWindow =
|
||||
MatrixState.pangeaController.userController.inTrialWindow;
|
||||
MatrixState.pangeaController.userController.inTrialWindow();
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class PaywallCard extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool inTrialWindow =
|
||||
MatrixState.pangeaController.userController.inTrialWindow;
|
||||
MatrixState.pangeaController.userController.inTrialWindow();
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
|
|
|
|||
|
|
@ -15,14 +15,16 @@ class SubscriptionButtons extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool inTrialWindow = pangeaController.userController.inTrialWindow;
|
||||
final bool inTrialWindow = pangeaController.userController.inTrialWindow();
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: controller
|
||||
.subscriptionController.subscription!.availableSubscriptions.length,
|
||||
itemCount: controller.subscriptionController.availableSubscriptionInfo!
|
||||
.availableSubscriptions.length,
|
||||
itemBuilder: (BuildContext context, int i) {
|
||||
final SubscriptionDetails subscription = pangeaController
|
||||
.subscriptionController.subscription!.availableSubscriptions[i];
|
||||
.subscriptionController
|
||||
.availableSubscriptionInfo!
|
||||
.availableSubscriptions[i];
|
||||
return Column(
|
||||
children: [
|
||||
ListTile(
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class SubscriptionOptions extends StatelessWidget {
|
|||
alignment: WrapAlignment.center,
|
||||
direction: Axis.horizontal,
|
||||
spacing: 10,
|
||||
children: pangeaController.userController.inTrialWindow
|
||||
children: pangeaController.userController.inTrialWindow()
|
||||
? [
|
||||
SubscriptionCard(
|
||||
onTap: () => pangeaController.subscriptionController
|
||||
|
|
@ -27,7 +27,7 @@ class SubscriptionOptions extends StatelessWidget {
|
|||
SubscriptionDetails(
|
||||
price: 0,
|
||||
id: "",
|
||||
periodType: 'trial',
|
||||
periodType: SubscriptionPeriodType.trial,
|
||||
),
|
||||
context,
|
||||
),
|
||||
|
|
@ -36,8 +36,8 @@ class SubscriptionOptions extends StatelessWidget {
|
|||
buttonText: L10n.of(context)!.activateTrial,
|
||||
),
|
||||
]
|
||||
: pangeaController
|
||||
.subscriptionController.subscription!.availableSubscriptions
|
||||
: pangeaController.subscriptionController.availableSubscriptionInfo!
|
||||
.availableSubscriptions
|
||||
.map(
|
||||
(subscription) => SubscriptionCard(
|
||||
subscription: subscription,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue