Merge branch 'main' into gh-actions-improvements

pull/795/head
Brord van Wierst 2 years ago
commit 1b6fa3a1c6
No known key found for this signature in database
GPG Key ID: 20E7ACBD8E02BC11

@ -33,6 +33,7 @@ jobs:
mv build/web public
touch public/.env
echo "$WEB_APP_ENV" >> public/.env
cp public/.env public/assets/.env
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:

@ -3339,7 +3339,12 @@
"downloadTxtFile": "Download Text File",
"downloadCSVFile": "Download CSV File",
"promotionalSubscriptionDesc": "You currently have a lifetime promotional subscription. Message support@pangea.chat for help changing your subscription.",
"originalSubscriptionPlatform": "Subscription purchased through",
"originalSubscriptionPlatform": "Subscription purchased through {purchasePlatform}",
"@originalSubscriptionPlatform": {
"placeholders": {
"purchasePlatform": {}
}
},
"oneWeekTrial": "One Week Trial",
"creatingSpacePleaseWait": "Creating space. Please wait...",
"downloadXLSXFile": "Download Excel File",
@ -3938,5 +3943,15 @@
"groupName": "Group name",
"createGroupAndInviteUsers": "Create a group and invite users",
"groupCanBeFoundViaSearch": "Group can be found via search",
"inNoSpaces": "You are not a member of any classes or exchanges"
"trialExpiration": "Your free trial expires on {expiration}",
"@trialExpiration": {
"placeholders": {
"expiration": {}
}
},
"freeTrialDesc": "New users recieve a one week free trial of Pangea Chat",
"activateTrial": "Activate Trial",
"inNoSpaces": "You are not a member of any classes or exchanges",
"successfullySubscribed": "You have successfully subscribed!",
"clickToManageSubscription": "Click here to manage your subscription."
}

@ -1,8 +1,8 @@
import 'dart:ui';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/config/environment.dart';
// Project imports:
import 'package:matrix/matrix.dart';
abstract class AppConfig {
// #Pangea
@ -121,6 +121,7 @@ abstract class AppConfig {
"https://buy.stripe.com/test_bIY6ssd8z5Uz8ec8ww";
static String iosPromoCode =
"https://apps.apple.com/redeem?ctx=offercodes&id=1445118630&code=";
static String trialSubscriptionId = "pangea_new_user_trial";
// Pangea#
static void loadFromJson(Map<String, dynamic> json) {

@ -1,9 +1,5 @@
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:go_router/go_router.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/archive/archive.dart';
import 'package:fluffychat/pages/chat/chat.dart';
@ -42,6 +38,9 @@ import 'package:fluffychat/widgets/layouts/empty_page.dart';
import 'package:fluffychat/widgets/layouts/two_column_layout.dart';
import 'package:fluffychat/widgets/log_view.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/cupertino.dart';
import 'package:go_router/go_router.dart';
import '../pangea/pages/analytics/class_analytics/class_analytics.dart';
import '../pangea/pages/analytics/class_list/class_list.dart';
@ -109,6 +108,16 @@ abstract class AppRoutes {
const LogViewer(),
),
),
// #Pangea
GoRoute(
path: '/user_age',
pageBuilder: (context, state) => defaultPageBuilder(
context,
const PUserAge(),
),
redirect: loggedOutRedirect,
),
// Pangea#
ShellRoute(
pageBuilder: (context, state, child) => defaultPageBuilder(
context,
@ -160,14 +169,6 @@ abstract class AppRoutes {
),
routes: [
// #Pangea
GoRoute(
path: 'user_age',
pageBuilder: (context, state) => defaultPageBuilder(
context,
const PUserAge(),
),
redirect: loggedOutRedirect,
),
GoRoute(
path: 'mylearning',
pageBuilder: (context, state) => defaultPageBuilder(

@ -1,19 +1,9 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:collection/collection.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
import 'package:uni_links/uni_links.dart';
// Project imports:
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat_list/chat_list_view.dart';
@ -24,10 +14,23 @@ import 'package:fluffychat/pangea/utils/add_to_space.dart';
import 'package:fluffychat/pangea/utils/chat_list_handle_space_tap.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/utils/firebase_analytics.dart';
import 'package:fluffychat/pangea/widgets/subscription/subscription_snackbar.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/client_stories_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/tor_stub.dart'
if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
import 'package:uni_links/uni_links.dart';
import '../../../utils/account_bundles.dart';
import '../../utils/matrix_sdk_extensions/matrix_file_extension.dart';
import '../../utils/url_launcher.dart';
@ -35,9 +38,6 @@ import '../../utils/voip/callkeep_manager.dart';
import '../../widgets/fluffy_chat_app.dart';
import '../../widgets/matrix.dart';
import 'package:fluffychat/utils/tor_stub.dart'
if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart';
enum SelectMode {
normal,
share,
@ -445,6 +445,7 @@ class ChatListController extends State<ChatList>
//#Pangea
StreamSubscription? classStream;
StreamSubscription? _invitedSpaceSubscription;
StreamSubscription? _subscriptionStatusStream;
//Pangea#
@override
@ -502,6 +503,14 @@ class ChatListController extends State<ChatList>
}
}
});
_subscriptionStatusStream = pangeaController
.subscriptionController.subscriptionStream.stream
.listen((event) {
if (mounted) {
showSubscribedSnackbar(context);
}
});
//Pangea#
super.initState();
@ -515,6 +524,7 @@ class ChatListController extends State<ChatList>
//#Pangea
classStream?.cancel();
_invitedSpaceSubscription?.cancel();
_subscriptionStatusStream?.cancel();
//Pangea#
scrollController.removeListener(_onScroll);
super.dispose();

@ -4,4 +4,8 @@ class PLocalKey {
static const String classes = 'classes';
static const String cachedClassCodeToJoin = "cachedclasscodetojoin";
static const String beganWebPayment = "beganWebPayment";
// making this a random string so that it's harder to guess
static const String activatedTrialKey = '7C4EuKIsph';
}

@ -1,16 +1,9 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:http/http.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:fluffychat/config/app_config.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/models/base_subscription_info.dart';
@ -22,6 +15,12 @@ import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/utils/firebase_analytics.dart';
import 'package:fluffychat/pangea/widgets/subscription/subscription_paywall.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:http/http.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
import 'package:url_launcher/url_launcher_string.dart';
class SubscriptionController extends BaseController {
late PangeaController _pangeaController;
@ -29,6 +28,7 @@ class SubscriptionController extends BaseController {
//convert this logic to use completer
bool initialized = false;
final StreamController subscriptionStream = StreamController.broadcast();
SubscriptionController(PangeaController pangeaController) : super() {
_pangeaController = pangeaController;
@ -59,13 +59,32 @@ class SubscriptionController extends BaseController {
: MobileSubscriptionInfo(pangeaController: _pangeaController);
await subscription!.configure();
if (activatedNewUserTrial) {
setNewUserTrial();
}
initialized = true;
if (!kIsWeb) {
Purchases.addCustomerInfoUpdateListener(
(CustomerInfo info) => updateCustomerInfo(),
(CustomerInfo info) async {
final bool wasSubscribed = isSubscribed;
await updateCustomerInfo();
if (!wasSubscribed && isSubscribed) {
subscriptionStream.add(true);
}
},
);
} else {
final bool? beganWebPayment = _pangeaController.pStoreService.read(
PLocalKey.beganWebPayment,
);
if (beganWebPayment ?? false) {
_pangeaController.pStoreService.delete(PLocalKey.beganWebPayment);
if (_pangeaController.subscriptionController.isSubscribed) {
subscriptionStream.add(true);
}
}
}
setState();
} catch (e, s) {
@ -74,6 +93,26 @@ class SubscriptionController extends BaseController {
}
}
bool get activatedNewUserTrial =>
_pangeaController.userController.inTrialWindow &&
(_pangeaController.pStoreService.read(PLocalKey.activatedTrialKey) ??
false);
void activateNewUserTrial() {
_pangeaController.pStoreService.save(PLocalKey.activatedTrialKey, true);
setNewUserTrial();
}
void setNewUserTrial() {
final String profileCreatedAt =
_pangeaController.userController.userModel!.profile!.createdAt;
final DateTime creationTimestamp = DateTime.parse(profileCreatedAt);
final DateTime expirationDate = creationTimestamp.add(
const Duration(days: 7),
);
subscription?.setTrial(expirationDate);
}
Future<void> updateCustomerInfo() async {
if (subscription == null) {
ErrorHandler.logError(
@ -175,6 +214,10 @@ class SubscriptionController extends BaseController {
selectedSubscription.duration!,
isPromo: isPromo,
);
_pangeaController.pStoreService.save(
PLocalKey.beganWebPayment,
true,
);
setState();
launchUrlString(
paymentLink,

@ -2,14 +2,15 @@ import 'dart:async';
import 'dart:developer';
import 'package:collection/collection.dart';
import 'package:jwt_decode/jwt_decode.dart';
import 'package:matrix/matrix.dart' as matrix;
import 'package:fluffychat/pangea/constants/language_keys.dart';
import 'package:fluffychat/pangea/constants/model_keys.dart';
import 'package:fluffychat/pangea/controllers/base_controller.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/widgets/fluffy_chat_app.dart';
// Project imports:
import 'package:jwt_decode/jwt_decode.dart';
import 'package:matrix/matrix.dart' as matrix;
import '../constants/local.key.dart';
import '../models/user_model.dart';
import '../repo/user_repo.dart';
@ -109,6 +110,16 @@ class UserController extends BaseController {
}
}
bool get inTrialWindow {
final String? createdAt = userModel?.profile?.createdAt;
if (createdAt == null) {
return false;
}
return DateTime.parse(createdAt).isAfter(
DateTime.now().subtract(const Duration(days: 7)),
);
}
Future<bool> get areUserLanguagesSet async {
try {
final PUserModel? toCheck = userModel ?? (await fetchUserModel());

@ -1,10 +1,9 @@
import 'dart:async';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../controllers/pangea_controller.dart';
class PAuthGaurd {
@ -19,7 +18,7 @@ class PAuthGaurd {
final bool setDob = await pController!
.userController.isUserDataAvailableAndDateOfBirthSet;
if (Matrix.of(context).client.isLogged()) {
return !setDob ? '/rooms/user_age' : '/rooms';
return !setDob ? '/user_age' : '/rooms';
}
return null;
} else {
@ -39,7 +38,7 @@ class PAuthGaurd {
return !Matrix.of(context).client.isLogged()
? '/home'
: !setDob
? '/rooms/user_age'
? '/user_age'
: null;
} else {
debugPrint("controller is null in pguard check");

@ -1,3 +1,5 @@
// Project imports:
import 'package:fluffychat/config/app_config.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';
@ -44,6 +46,9 @@ class SubscriptionInfo {
allProducts = await SubscriptionRepo.getAllProducts();
}
bool get isNewUserTrial =>
currentSubscriptionId == AppConfig.trialSubscriptionId;
bool get currentSubscriptionIsPromotional =>
currentSubscriptionId?.startsWith("rc_promo") ?? false;
@ -70,5 +75,16 @@ class SubscriptionInfo {
currentSubscriptionId = null;
}
void setTrial(DateTime expiration) {
if (currentSubscription != null) return;
expirationDate = expiration;
currentSubscriptionId = AppConfig.trialSubscriptionId;
currentSubscription = SubscriptionDetails(
price: 0,
id: AppConfig.trialSubscriptionId,
periodType: 'trial',
);
}
Future<void> setCustomerInfo() async {}
}

@ -1,15 +1,15 @@
import 'dart:io';
import 'package:flutter/material.dart';
// Package imports:
import 'package:collection/collection.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
import 'package:sentry_flutter/sentry_flutter.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';
import 'package:fluffychat/pangea/utils/error_handler.dart';
// Project imports:
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();
@ -182,7 +182,9 @@ class MobileSubscriptionInfo extends SubscriptionInfo {
);
} else if (activeEntitlements.isEmpty) {
debugPrint("User has no active entitlements");
resetSubscription();
if (!isNewUserTrial) {
resetSubscription();
}
return;
}
final EntitlementInfo activeEntitlement = activeEntitlements[0];

@ -1,9 +1,9 @@
import 'package:fluffychat/pangea/pages/p_user_age/p_user_age.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:intl/intl.dart';
import 'package:syncfusion_flutter_datepicker/datepicker.dart';
import 'package:fluffychat/pangea/pages/p_user_age/p_user_age.dart';
import '../../../widgets/layouts/login_scaffold.dart';
class PUserAgeView extends StatelessWidget {
@ -12,7 +12,6 @@ class PUserAgeView extends StatelessWidget {
@override
Widget build(BuildContext context) {
controller.dobController.text = "";
return LoginScaffold(
appBar: AppBar(
automaticallyImplyLeading: !controller.loading,
@ -45,21 +44,52 @@ class PUserAgeView extends StatelessWidget {
controller.loading ? null : [AutofillHints.birthday],
validator: controller.dobFieldValidator,
onTap: () async {
final DateTime? pickedDate = await showDatePicker(
initialDatePickerMode: DatePickerMode.year,
showDialog(
context: context,
initialDate: controller.initialDate,
firstDate: DateTime(1940),
lastDate: DateTime.now(),
builder: (BuildContext context) {
return Center(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
color: Theme.of(context).colorScheme.background,
),
padding: const EdgeInsets.all(12),
// height: 350,
width: 500,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Card(
child: SfDateRangePicker(
view: DateRangePickerView.month,
showNavigationArrow: true,
showActionButtons: true,
selectionMode:
DateRangePickerSelectionMode.single,
initialDisplayDate: controller.initialDate,
initialSelectedDate: controller.initialDate,
onSubmit: (val) {
final DateTime? pickedDate =
val as DateTime?;
if (pickedDate != null) {
controller.dobController.text =
DateFormat.yMd().format(pickedDate);
controller.error = null;
} else {
controller.error =
L10n.of(context)!.invalidDob;
}
Navigator.pop(context);
},
onCancel: () => Navigator.pop(context),
),
),
],
),
),
);
},
);
if (pickedDate != null) {
controller.dobController.text =
DateFormat.yMd().format(pickedDate);
controller.error = null;
} else {
controller.error = L10n.of(context)!.invalidDob;
}
},
// onChanged: (String newValue) {
// try {

@ -1,16 +1,19 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher_string.dart';
// Project imports:
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/pages/settings_subscription/settings_subscription_view.dart';
import 'package:fluffychat/pangea/utils/subscription_app_id.dart';
import 'package:fluffychat/pangea/widgets/subscription/subscription_snackbar.dart';
import 'package:fluffychat/widgets/matrix.dart';
// Project imports:
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
// Package imports:
import 'package:url_launcher/url_launcher_string.dart';
class SubscriptionManagement extends StatefulWidget {
const SubscriptionManagement({super.key});
@ -24,6 +27,7 @@ class SubscriptionManagementController extends State<SubscriptionManagement> {
final PangeaController pangeaController = MatrixState.pangeaController;
SubscriptionDetails? selectedSubscription;
late StreamSubscription _settingsSubscription;
StreamSubscription? _subscriptionStatusStream;
@override
void initState() {
@ -40,6 +44,7 @@ class SubscriptionManagementController extends State<SubscriptionManagement> {
void dispose() {
super.dispose();
_settingsSubscription.cancel();
_subscriptionStatusStream?.cancel();
}
bool get currentSubscriptionAvailable =>
@ -53,6 +58,10 @@ class SubscriptionManagementController extends State<SubscriptionManagement> {
?.currentSubscriptionIsPromotional ??
false;
bool get isNewUserTrial =>
pangeaController.subscriptionController.subscription?.isNewUserTrial ??
false;
bool get showManagementOptions {
if (!currentSubscriptionAvailable) {
return false;
@ -116,6 +125,12 @@ class SubscriptionManagementController extends State<SubscriptionManagement> {
@override
Widget build(BuildContext context) {
_subscriptionStatusStream ??= pangeaController
.subscriptionController.subscriptionStream.stream
.listen((_) {
showSubscribedSnackbar(context);
context.go('/rooms');
});
return SettingsSubscriptionView(this);
}
}

@ -1,14 +1,14 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:intl/intl.dart';
// Flutter imports:
// Project imports:
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/controllers/subscription_controller.dart';
import 'package:fluffychat/pangea/pages/settings_subscription/change_subscription.dart';
import 'package:fluffychat/pangea/pages/settings_subscription/settings_subscription.dart';
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:intl/intl.dart';
class SettingsSubscriptionView extends StatelessWidget {
final SubscriptionManagementController controller;
@ -105,45 +105,44 @@ class ManagementNotAvailableWarning extends StatelessWidget {
@override
Widget build(BuildContext context) {
final bool currentSubscriptionAvailable =
controller.currentSubscriptionAvailable;
final bool currentSubscriptionIsPromotional =
controller.currentSubscriptionIsPromotional;
final String? purchasePlatformDisplayName =
controller.purchasePlatformDisplayName;
final bool isLifetimeSubscription =
subscriptionController.subscription?.isLifetimeSubscription ?? false;
final DateTime? expirationDate =
subscriptionController.subscription?.expirationDate;
String warningText = L10n.of(context)!.subscriptionManagementUnavailable;
final DateFormat formatter = DateFormat('yyyy-MM-dd');
if (currentSubscriptionAvailable) {
warningText = L10n.of(context)!.subsciptionPlatformTooltip;
} else if (currentSubscriptionIsPromotional) {
if (isLifetimeSubscription) {
warningText = L10n.of(context)!.promotionalSubscriptionDesc;
} else {
warningText = L10n.of(context)!.promoSubscriptionExpirationDesc(
formatter.format(expirationDate!),
String getWarningText() {
final DateFormat formatter = DateFormat('yyyy-MM-dd');
if (controller.isNewUserTrial) {
return L10n.of(context)!.trialExpiration(
formatter.format(
subscriptionController.subscription!.expirationDate!,
),
);
}
if (controller.currentSubscriptionAvailable) {
String warningText = L10n.of(context)!.subsciptionPlatformTooltip;
if (controller.purchasePlatformDisplayName != null) {
warningText +=
"\n${L10n.of(context)!.originalSubscriptionPlatform(controller.purchasePlatformDisplayName!)}";
}
return warningText;
}
if (controller.currentSubscriptionIsPromotional) {
if (subscriptionController.subscription?.isLifetimeSubscription ??
false) {
return L10n.of(context)!.promotionalSubscriptionDesc;
}
return L10n.of(context)!.promoSubscriptionExpirationDesc(
formatter.format(
subscriptionController.subscription!.expirationDate!,
),
);
}
return L10n.of(context)!.subscriptionManagementUnavailable;
}
return Center(
child: Column(
children: [
Text(
warningText,
textAlign: TextAlign.center,
),
if (purchasePlatformDisplayName != null)
Text(
"${L10n.of(context)!.originalSubscriptionPlatform} $purchasePlatformDisplayName",
textAlign: TextAlign.center,
),
],
child: Padding(
padding: const EdgeInsets.all(20),
child: Text(
getWarningText(),
textAlign: TextAlign.center,
),
),
);
}

@ -1,10 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
// Flutter imports:
// Project imports:
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/controllers/subscription_controller.dart';
import 'package:flutter/material.dart';
// Package imports:
import 'package:flutter_gen/gen_l10n/l10n.dart';
class SubscriptionOptions extends StatelessWidget {
final PangeaController pangeaController;
@ -16,23 +17,18 @@ class SubscriptionOptions extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Expanded(
child: ListView(
children: [
const SizedBox(height: 20),
Wrap(
alignment: WrapAlignment.center,
direction: Axis.horizontal,
children: pangeaController
.subscriptionController.subscription!.availableSubscriptions
.map(
(subscription) => SubscriptionCard(
subscription: subscription,
pangeaController: pangeaController,
),
)
.toList(),
),
],
child: Wrap(
alignment: WrapAlignment.center,
direction: Axis.horizontal,
children: pangeaController
.subscriptionController.subscription!.availableSubscriptions
.map(
(subscription) => SubscriptionCard(
subscription: subscription,
pangeaController: pangeaController,
),
)
.toList(),
),
);
}
@ -80,7 +76,11 @@ class SubscriptionCard extends StatelessWidget {
OutlinedButton(
onPressed: () {
pangeaController.subscriptionController
.submitSubscriptionChange(subscription, context);
.submitSubscriptionChange(
subscription,
context,
);
Navigator.of(context).pop();
},
child: Text(L10n.of(context)!.subscribe),
),

@ -1,9 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
// Flutter imports:
// Project imports:
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/widgets/subscription/subscription_options.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
class SubscriptionPaywall extends StatelessWidget {
final PangeaController pangeaController;
@ -28,7 +29,7 @@ class SubscriptionPaywall extends StatelessWidget {
),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
child: ListView(
children: [
if (pangeaController.matrixState.client.rooms.length > 1) ...[
Text(
@ -44,12 +45,65 @@ class SubscriptionPaywall extends StatelessWidget {
style: const TextStyle(fontSize: 16),
),
const SizedBox(height: 20),
SubscriptionOptions(
pangeaController: pangeaController,
),
pangeaController.userController.inTrialWindow
? FreeTrialCard(
pangeaController: pangeaController,
)
: SubscriptionOptions(
pangeaController: pangeaController,
),
],
),
),
);
}
}
class FreeTrialCard extends StatelessWidget {
final PangeaController pangeaController;
const FreeTrialCard({super.key, required this.pangeaController});
@override
Widget build(BuildContext context) {
return Align(
child: Card(
shape: RoundedRectangleBorder(
side: BorderSide(
color: AppConfig.primaryColorLight.withAlpha(64),
),
borderRadius: const BorderRadius.all(Radius.zero),
),
child: SizedBox(
height: 250,
width: AppConfig.columnWidth * 0.75,
child: Padding(
padding: const EdgeInsets.all(25),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
L10n.of(context)!.freeTrial,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 24),
),
Text(
L10n.of(context)!.freeTrialDesc,
textAlign: TextAlign.center,
),
OutlinedButton(
onPressed: () {
pangeaController.subscriptionController
.activateNewUserTrial();
Navigator.of(context).pop();
},
child: Text(L10n.of(context)!.activateTrial),
),
],
),
),
),
),
);
}
}

@ -0,0 +1,34 @@
import 'package:fluffychat/config/app_config.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:go_router/go_router.dart';
void showSubscribedSnackbar(BuildContext context) {
final Widget text = RichText(
text: TextSpan(
children: [
TextSpan(
text: L10n.of(context)!.successfullySubscribed,
style: TextStyle(
color: Theme.of(context).brightness == Brightness.light
? Colors.white
: Colors.black,
),
),
const TextSpan(text: " "),
TextSpan(
text: L10n.of(context)!.clickToManageSubscription,
style: const TextStyle(color: AppConfig.primaryColor),
recognizer: TapGestureRecognizer()
..onTap = () => context.go('/rooms/settings/subscription'),
),
],
),
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: text,
),
);
}

@ -2,12 +2,19 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:collection/collection.dart';
import 'package:desktop_notifications/desktop_notifications.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/utils/any_state_holder.dart';
import 'package:fluffychat/utils/client_manager.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/uia_request_manager.dart';
import 'package:fluffychat/utils/voip_plugin.dart';
import 'package:fluffychat/widgets/fluffy_chat_app.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:http/http.dart' as http;
@ -19,14 +26,6 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:universal_html/html.dart' as html;
import 'package:url_launcher/url_launcher_string.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/utils/any_state_holder.dart';
import 'package:fluffychat/utils/client_manager.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/uia_request_manager.dart';
import 'package:fluffychat/utils/voip_plugin.dart';
import 'package:fluffychat/widgets/fluffy_chat_app.dart';
import '../config/app_config.dart';
import '../config/setting_keys.dart';
import '../pages/key_verification/key_verification_dialog.dart';
@ -354,7 +353,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
routeDestination = await pangeaController
.userController.isUserDataAvailableAndDateOfBirthSet
? '/rooms'
: "/rooms/user_age";
: "/user_age";
} else {
routeDestination = '/home';
}

@ -756,7 +756,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"bn": [
@ -1521,7 +1526,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"bo": [
@ -2286,7 +2296,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"ca": [
@ -3046,7 +3061,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"cs": [
@ -3806,7 +3826,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"de": [
@ -4566,7 +4591,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"el": [
@ -5331,7 +5361,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"eo": [
@ -6091,7 +6126,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"es": [
@ -6115,7 +6155,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"et": [
@ -6875,7 +6920,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"eu": [
@ -7635,7 +7685,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"fa": [
@ -8395,7 +8450,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"fi": [
@ -9155,7 +9215,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"fr": [
@ -9915,7 +9980,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"ga": [
@ -10675,7 +10745,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"gl": [
@ -11435,7 +11510,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"he": [
@ -12195,7 +12275,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"hi": [
@ -12960,7 +13045,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"hr": [
@ -13720,7 +13810,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"hu": [
@ -14480,7 +14575,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"id": [
@ -15240,7 +15340,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"ie": [
@ -16002,7 +16107,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"it": [
@ -16762,7 +16872,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"ja": [
@ -17522,7 +17637,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"ko": [
@ -18282,7 +18402,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"lt": [
@ -19042,7 +19167,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"lv": [
@ -19807,7 +19937,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"nb": [
@ -20567,7 +20702,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"nl": [
@ -21327,7 +21467,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"pl": [
@ -22087,7 +22232,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"pt": [
@ -22852,7 +23002,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"pt_BR": [
@ -23612,7 +23767,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"pt_PT": [
@ -24372,7 +24532,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"ro": [
@ -25132,7 +25297,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"ru": [
@ -25892,7 +26062,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"sk": [
@ -26653,7 +26828,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"sl": [
@ -27416,7 +27596,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"sr": [
@ -28176,7 +28361,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"sv": [
@ -28936,7 +29126,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"ta": [
@ -29701,7 +29896,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"th": [
@ -30466,7 +30666,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"tr": [
@ -31226,7 +31431,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"uk": [
@ -31986,7 +32196,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"vi": [
@ -32749,7 +32964,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"zh": [
@ -33509,7 +33729,12 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
],
"zh_Hant": [
@ -34269,6 +34494,11 @@
"groupName",
"createGroupAndInviteUsers",
"groupCanBeFoundViaSearch",
"inNoSpaces"
"trialExpiration",
"freeTrialDesc",
"activateTrial",
"inNoSpaces",
"successfullySubscribed",
"clickToManageSubscription"
]
}

@ -2047,26 +2047,34 @@ packages:
dependency: transitive
description:
name: syncfusion_flutter_core
sha256: "9f0a4593f7642b2f106e329734d0e5fc746baf8d0a59495eec586cd0d9ba7d02"
sha256: a2427697bfad5b611db78ea4c4daef82d3350b83c729a8dc37959662a31547f9
url: "https://pub.dev"
source: hosted
version: "22.2.12"
version: "23.2.7"
syncfusion_flutter_datepicker:
dependency: "direct main"
description:
name: syncfusion_flutter_datepicker
sha256: b3340a7786f674d18bd22c226358648985e7631734dfc4aae09fdcfb71c09156
url: "https://pub.dev"
source: hosted
version: "23.2.7"
syncfusion_flutter_xlsio:
dependency: "direct main"
description:
name: syncfusion_flutter_xlsio
sha256: "66b009fce91e10cfa5d9b3cdf2c4aa3fdf7430dab159626f4c67297638da2caf"
sha256: "84c771ce3684820e930d27604e801991efaa0e7a72ee1df4ba4c843258f16285"
url: "https://pub.dev"
source: hosted
version: "22.2.12"
version: "23.2.7"
syncfusion_officecore:
dependency: transitive
description:
name: syncfusion_officecore
sha256: "66d0a0faba40f043bba4ef102474213d3145ffeca2b3f2351a98c0cc10079c27"
sha256: a037c8a72748332ba28723eaed2fd1656b9acdaf03c23c6a08e87f7c31dbed18
url: "https://pub.dev"
source: hosted
version: "22.2.12"
version: "23.2.7"
synchronized:
dependency: transitive
description:

@ -99,7 +99,8 @@ dependencies:
shared_preferences: ^2.2.0 # Pinned because https://github.com/flutter/flutter/issues/118401
slugify: ^2.0.0
swipe_to_action: ^0.2.0
syncfusion_flutter_xlsio: ^22.2.9
syncfusion_flutter_datepicker: ^23.2.7
syncfusion_flutter_xlsio: ^23.2.7
tor_detector_web: ^1.1.0
uni_links: ^0.5.1
unifiedpush: ^5.0.1
@ -132,7 +133,6 @@ flutter:
generate: true
uses-material-design: true
assets:
- .env
- assets/
- assets/pangea/
- assets/pangea/bot_faces/

@ -2,5 +2,4 @@
flutter config --enable-web
flutter clean
flutter pub get
flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --profile --source-maps
# flutter build web --release --verbose --source-maps --dart-define=SENTRY_RELEASE=$CI_COMMIT_SHA
flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --profile --source-maps
Loading…
Cancel
Save