refactor: move local cache of subscription info into its own repo, dismiss paywall on show initial paywall card (#4532)
This commit is contained in:
parent
a2a81733bd
commit
c67dc2ab18
8 changed files with 120 additions and 143 deletions
|
|
@ -42,7 +42,11 @@ void main() async {
|
|||
/// Then where ever you need language functions simply call PangeaLanguage pangeaLanguage = PangeaLanguage()
|
||||
/// pangeaLanguage.getList or whatever function you need
|
||||
///
|
||||
await GetStorage.init();
|
||||
final List<Future> initFutures = [
|
||||
GetStorage.init(),
|
||||
GetStorage.init("subscription_storage"),
|
||||
];
|
||||
await Future.wait(initFutures);
|
||||
// Pangea#
|
||||
|
||||
// Our background push shared isolate accesses flutter-internal things very early in the startup proccess
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import 'package:fluffychat/pangea/choreographer/widgets/igc/paywall_card.dart';
|
|||
import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/any_state_holder.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/overlay.dart';
|
||||
import 'package:fluffychat/pangea/events/models/representation_content_model.dart';
|
||||
import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart';
|
||||
import 'package:fluffychat/pangea/events/repo/token_api_models.dart';
|
||||
|
|
@ -107,15 +106,7 @@ class Choreographer {
|
|||
// show the paywall if applicable or just send the message
|
||||
final status = pangeaController.subscriptionController.subscriptionStatus;
|
||||
status == SubscriptionStatus.shouldShowPaywall
|
||||
? OverlayUtil.showPositionedCard(
|
||||
context: context,
|
||||
cardToShow: PaywallCard(
|
||||
chatController: chatController,
|
||||
),
|
||||
maxHeight: 325,
|
||||
maxWidth: 325,
|
||||
transformTargetId: inputTransformTargetKey,
|
||||
)
|
||||
? PaywallCard.show(context, chatController)
|
||||
: chatController.send(
|
||||
message: chatController.sendController.text,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -50,15 +50,8 @@ class PangeaTextController extends TextEditingController {
|
|||
SubscriptionStatus.shouldShowPaywall &&
|
||||
!choreographer.isFetching &&
|
||||
text.isNotEmpty) {
|
||||
OverlayUtil.showPositionedCard(
|
||||
context: context,
|
||||
cardToShow: PaywallCard(
|
||||
chatController: choreographer.chatController,
|
||||
),
|
||||
maxHeight: 325,
|
||||
maxWidth: 325,
|
||||
transformTargetId: choreographer.inputTransformTargetKey,
|
||||
);
|
||||
PaywallCard.show(context, choreographer.chatController);
|
||||
return;
|
||||
}
|
||||
|
||||
// if there is no igc text data, then don't do anything
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import 'package:fluffychat/pages/chat/chat.dart';
|
|||
import 'package:fluffychat/pangea/bot/utils/bot_style.dart';
|
||||
import 'package:fluffychat/pangea/bot/widgets/bot_face_svg.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/igc/card_header.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/overlay.dart';
|
||||
import 'package:fluffychat/pangea/subscription/repo/subscription_management_repo.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class PaywallCard extends StatelessWidget {
|
||||
|
|
@ -14,6 +16,27 @@ class PaywallCard extends StatelessWidget {
|
|||
required this.chatController,
|
||||
});
|
||||
|
||||
static Future<void> show(
|
||||
BuildContext context,
|
||||
ChatController chatController,
|
||||
) async {
|
||||
if (!MatrixState
|
||||
.pangeaController.subscriptionController.shouldShowPaywall) {
|
||||
return;
|
||||
}
|
||||
|
||||
await SubscriptionManagementRepo.setDismissedPaywall();
|
||||
OverlayUtil.showPositionedCard(
|
||||
context: context,
|
||||
cardToShow: PaywallCard(
|
||||
chatController: chatController,
|
||||
),
|
||||
maxHeight: 325,
|
||||
maxWidth: 325,
|
||||
transformTargetId: chatController.choreographer.inputTransformTargetKey,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool inTrialWindow =
|
||||
|
|
@ -26,10 +49,6 @@ class PaywallCard extends StatelessWidget {
|
|||
CardHeader(
|
||||
text: L10n.of(context).clickMessageTitle,
|
||||
botExpression: BotExpression.addled,
|
||||
onClose: () {
|
||||
MatrixState.pangeaController.subscriptionController
|
||||
.dismissPaywall();
|
||||
},
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import 'package:fluffychat/config/themes.dart';
|
|||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/enums/assistance_state_enum.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/igc/paywall_card.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/overlay.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart';
|
||||
import '../../../pages/chat/chat.dart';
|
||||
|
||||
|
|
@ -81,16 +80,7 @@ class StartIGCButtonState extends State<StartIGCButton>
|
|||
Future<void> _onTap() async {
|
||||
switch (assistanceState) {
|
||||
case AssistanceState.noSub:
|
||||
OverlayUtil.showPositionedCard(
|
||||
context: context,
|
||||
cardToShow: PaywallCard(
|
||||
chatController: widget.controller,
|
||||
),
|
||||
maxHeight: 325,
|
||||
maxWidth: 325,
|
||||
transformTargetId:
|
||||
widget.controller.choreographer.inputTransformTargetKey,
|
||||
);
|
||||
await PaywallCard.show(context, widget.controller);
|
||||
return;
|
||||
case AssistanceState.noMessage:
|
||||
showDialog(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:purchases_flutter/purchases_flutter.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
|
@ -13,7 +12,6 @@ import 'package:url_launcher/url_launcher_string.dart';
|
|||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/common/constants/local.key.dart';
|
||||
import 'package:fluffychat/pangea/common/controllers/base_controller.dart';
|
||||
import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/common/network/requests.dart';
|
||||
|
|
@ -23,6 +21,7 @@ import 'package:fluffychat/pangea/common/utils/firebase_analytics.dart';
|
|||
import 'package:fluffychat/pangea/subscription/models/base_subscription_info.dart';
|
||||
import 'package:fluffychat/pangea/subscription/models/mobile_subscriptions.dart';
|
||||
import 'package:fluffychat/pangea/subscription/models/web_subscriptions.dart';
|
||||
import 'package:fluffychat/pangea/subscription/repo/subscription_management_repo.dart';
|
||||
import 'package:fluffychat/pangea/subscription/repo/subscription_repo.dart';
|
||||
import 'package:fluffychat/pangea/subscription/utils/subscription_app_id.dart';
|
||||
import 'package:fluffychat/pangea/subscription/widgets/subscription_paywall.dart';
|
||||
|
|
@ -37,7 +36,6 @@ enum SubscriptionStatus {
|
|||
}
|
||||
|
||||
class SubscriptionController extends BaseController {
|
||||
static final GetStorage subscriptionBox = GetStorage("subscription_storage");
|
||||
late PangeaController _pangeaController;
|
||||
|
||||
CurrentSubscriptionInfo? currentSubscriptionInfo;
|
||||
|
|
@ -129,12 +127,8 @@ class SubscriptionController extends BaseController {
|
|||
},
|
||||
);
|
||||
} else {
|
||||
final bool? beganWebPayment =
|
||||
subscriptionBox.read(PLocalKey.beganWebPayment);
|
||||
if (beganWebPayment ?? false) {
|
||||
await subscriptionBox.remove(
|
||||
PLocalKey.beganWebPayment,
|
||||
);
|
||||
if (SubscriptionManagementRepo.getBeganWebPayment()) {
|
||||
await SubscriptionManagementRepo.removeBeganWebPayment();
|
||||
if (isSubscribed != null && isSubscribed!) {
|
||||
subscriptionStream.add(true);
|
||||
}
|
||||
|
|
@ -202,10 +196,7 @@ class SubscriptionController extends BaseController {
|
|||
selectedSubscription.duration!,
|
||||
isPromo: isPromo,
|
||||
);
|
||||
await subscriptionBox.write(
|
||||
PLocalKey.beganWebPayment,
|
||||
true,
|
||||
);
|
||||
await SubscriptionManagementRepo.setBeganWebPayment();
|
||||
setState(null);
|
||||
launchUrlString(
|
||||
paymentLink,
|
||||
|
|
@ -255,54 +246,16 @@ class SubscriptionController extends BaseController {
|
|||
|
||||
return isSubscribed!
|
||||
? SubscriptionStatus.subscribed
|
||||
: _shouldShowPaywall
|
||||
: shouldShowPaywall
|
||||
? SubscriptionStatus.shouldShowPaywall
|
||||
: SubscriptionStatus.dimissedPaywall;
|
||||
}
|
||||
|
||||
DateTime? get _lastDismissedPaywall {
|
||||
final lastDismissed = subscriptionBox.read(
|
||||
PLocalKey.dismissedPaywall,
|
||||
);
|
||||
if (lastDismissed == null) return null;
|
||||
return DateTime.tryParse(lastDismissed);
|
||||
}
|
||||
|
||||
int? get _paywallBackoff {
|
||||
final backoff = subscriptionBox.read(
|
||||
PLocalKey.paywallBackoff,
|
||||
);
|
||||
if (backoff == null) return null;
|
||||
return backoff;
|
||||
}
|
||||
|
||||
/// whether or not the paywall should be shown
|
||||
bool get _shouldShowPaywall {
|
||||
bool get shouldShowPaywall {
|
||||
return initCompleter.isCompleted &&
|
||||
isSubscribed != null &&
|
||||
!isSubscribed! &&
|
||||
(_lastDismissedPaywall == null ||
|
||||
DateTime.now().difference(_lastDismissedPaywall!).inHours >
|
||||
(1 * (_paywallBackoff ?? 1)));
|
||||
}
|
||||
|
||||
void dismissPaywall() async {
|
||||
await subscriptionBox.write(
|
||||
PLocalKey.dismissedPaywall,
|
||||
DateTime.now().toString(),
|
||||
);
|
||||
|
||||
if (_paywallBackoff == null) {
|
||||
await subscriptionBox.write(
|
||||
PLocalKey.paywallBackoff,
|
||||
1,
|
||||
);
|
||||
} else {
|
||||
await subscriptionBox.write(
|
||||
PLocalKey.paywallBackoff,
|
||||
_paywallBackoff! + 1,
|
||||
);
|
||||
}
|
||||
isSubscribed == false &&
|
||||
!SubscriptionManagementRepo.getDismissedPaywall();
|
||||
}
|
||||
|
||||
Future<void> showPaywall(BuildContext context) async {
|
||||
|
|
@ -330,7 +283,7 @@ class SubscriptionController extends BaseController {
|
|||
);
|
||||
},
|
||||
);
|
||||
dismissPaywall();
|
||||
await SubscriptionManagementRepo.setDismissedPaywall();
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
|
|
@ -369,37 +322,6 @@ class SubscriptionController extends BaseController {
|
|||
String? get defaultManagementURL =>
|
||||
currentSubscriptionInfo?.currentSubscription
|
||||
?.defaultManagementURL(availableSubscriptionInfo?.appIds);
|
||||
|
||||
Future<void> setCachedSubscriptionInfo(
|
||||
AvailableSubscriptionsInfo info,
|
||||
) =>
|
||||
subscriptionBox.write(
|
||||
PLocalKey.availableSubscriptionInfo,
|
||||
info.toJson(),
|
||||
);
|
||||
|
||||
Future<AvailableSubscriptionsInfo?> getCachedSubscriptionInfo() async {
|
||||
final entry = subscriptionBox.read(
|
||||
PLocalKey.availableSubscriptionInfo,
|
||||
);
|
||||
if (entry is! Map<String, dynamic>) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
final resp = AvailableSubscriptionsInfo.fromJson(entry);
|
||||
return resp.lastUpdated == null ? null : resp;
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"entry": entry,
|
||||
},
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum SubscriptionDuration {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import 'package:collection/collection.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart';
|
||||
import 'package:fluffychat/pangea/subscription/repo/subscription_management_repo.dart';
|
||||
import 'package:fluffychat/pangea/subscription/repo/subscription_repo.dart';
|
||||
import 'package:fluffychat/pangea/subscription/utils/subscription_app_id.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
/// Contains information about the users's current subscription
|
||||
class CurrentSubscriptionInfo {
|
||||
|
|
@ -78,13 +77,16 @@ class AvailableSubscriptionsInfo {
|
|||
});
|
||||
|
||||
Future<void> setAvailableSubscriptions() async {
|
||||
final cachedInfo = await MatrixState.pangeaController.subscriptionController
|
||||
.getCachedSubscriptionInfo();
|
||||
final cachedInfo =
|
||||
SubscriptionManagementRepo.getAvailableSubscriptionsInfo();
|
||||
|
||||
appIds ??= cachedInfo?.appIds ?? await SubscriptionRepo.getAppIds();
|
||||
allProducts ??=
|
||||
cachedInfo?.allProducts ?? await SubscriptionRepo.getAllProducts();
|
||||
|
||||
if (cachedInfo == null) await _cacheSubscriptionInfo();
|
||||
if (cachedInfo == null) {
|
||||
await SubscriptionManagementRepo.setAvailableSubscriptionsInfo(this);
|
||||
}
|
||||
|
||||
availableSubscriptions = (allProducts ?? [])
|
||||
.where(
|
||||
|
|
@ -95,22 +97,6 @@ class AvailableSubscriptionsInfo {
|
|||
.toList();
|
||||
}
|
||||
|
||||
Future<void> _cacheSubscriptionInfo() async {
|
||||
try {
|
||||
MatrixState.pangeaController.subscriptionController
|
||||
.setCachedSubscriptionInfo(this);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"appIds": appIds,
|
||||
"allProducts": allProducts,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
factory AvailableSubscriptionsInfo.fromJson(Map<String, dynamic> json) {
|
||||
if (!json.containsKey('app_ids') || !json.containsKey('all_products')) {
|
||||
throw "Cached subscription info is missing required fields";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
import 'package:get_storage/get_storage.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/constants/local.key.dart';
|
||||
import 'package:fluffychat/pangea/subscription/models/base_subscription_info.dart';
|
||||
|
||||
class SubscriptionManagementRepo {
|
||||
static final GetStorage _cache = GetStorage("subscription_storage");
|
||||
|
||||
static AvailableSubscriptionsInfo? getAvailableSubscriptionsInfo() {
|
||||
final entry = _cache.read(PLocalKey.availableSubscriptionInfo);
|
||||
if (entry == null) return null;
|
||||
try {
|
||||
return AvailableSubscriptionsInfo.fromJson(entry);
|
||||
} catch (e) {
|
||||
_cache.remove(PLocalKey.availableSubscriptionInfo);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> setAvailableSubscriptionsInfo(
|
||||
AvailableSubscriptionsInfo info,
|
||||
) async {
|
||||
await _cache.write(
|
||||
PLocalKey.availableSubscriptionInfo,
|
||||
info.toJson(),
|
||||
);
|
||||
}
|
||||
|
||||
static bool getBeganWebPayment() {
|
||||
return _cache.read(PLocalKey.beganWebPayment) ?? false;
|
||||
}
|
||||
|
||||
static Future<void> setBeganWebPayment() async {
|
||||
await _cache.write(PLocalKey.beganWebPayment, true);
|
||||
}
|
||||
|
||||
static Future<void> removeBeganWebPayment() async {
|
||||
await _cache.remove(PLocalKey.beganWebPayment);
|
||||
}
|
||||
|
||||
static bool getDismissedPaywall() {
|
||||
final entry = _cache.read(PLocalKey.dismissedPaywall);
|
||||
if (entry == null) return false;
|
||||
try {
|
||||
final dismissed = DateTime.parse(entry);
|
||||
final nextValidShowtime = dismissed.add(
|
||||
Duration(hours: 1 * _getPaywallBackoff()),
|
||||
);
|
||||
return DateTime.now().isBefore(nextValidShowtime);
|
||||
} catch (e) {
|
||||
_cache.remove(PLocalKey.dismissedPaywall);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> setDismissedPaywall() async {
|
||||
await _cache.write(
|
||||
PLocalKey.dismissedPaywall,
|
||||
DateTime.now().toIso8601String(),
|
||||
);
|
||||
await _incrementPaywallBackoff();
|
||||
}
|
||||
|
||||
static int _getPaywallBackoff() {
|
||||
return _cache.read(PLocalKey.paywallBackoff) ?? 0;
|
||||
}
|
||||
|
||||
static Future<void> _incrementPaywallBackoff() async {
|
||||
final int backoff = _getPaywallBackoff() + 1;
|
||||
await _cache.write(PLocalKey.paywallBackoff, backoff);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue