Merge branch 'main' into sentry
This commit is contained in:
commit
4c00c73bea
27 changed files with 685 additions and 280 deletions
27
README.md
27
README.md
|
|
@ -1,3 +1,30 @@
|
|||
Pangea Chat Client Setup:
|
||||
|
||||
* Download VSCode if you do not already have it installed
|
||||
* Download flutter on your device using this guide: https://docs.flutter.dev/get-started/install
|
||||
* Test to make sure that flutter is properly installed by running “flutter –version”
|
||||
* You may need to add flutter to your path manually. Instructions can be found here: https://docs.flutter.dev/get-started/install/macos/mobile-ios?tab=download#add-flutter-to-your-path
|
||||
* Ensure that Google Chrome is installed
|
||||
* Install the latest version of XCode
|
||||
* After downloading XCode, ensure that the iOS simulator runtime is installed. To do this, after initially downloading XCode, a screen will open where you can select the platforms you wish to develop for. Selected iOS and download from there.
|
||||
* Install the latest version of Android Studio
|
||||
* After downloading Android Studio, open Android Studio and go through setup wizard
|
||||
* In Android Studio, open settings -> Android SDK -> SDK tools, then click “Android SDK Command Line Tools” and click OK to run the download
|
||||
* If you do not have homebrew install on your device, install homebrew by follow the instructions here: https://brew.sh/
|
||||
* Run “brew install cocoapods” to install cocoapods
|
||||
* Run “flutter doctor” and for any missing components, follow the instructions from the print out to install / setup
|
||||
* Clone the client repo
|
||||
* Copy the .env file (and the .env.prod file, if you want to run production builds), into the root folder of the client and the assets/ folder
|
||||
* Uncomment the lines in the pubspec.yaml file in the assets section with paths to .env file
|
||||
* To run on iOS:
|
||||
* Run “flutter precache --ios”
|
||||
* Go to the iOS folder and run “pod install”
|
||||
* To run on Android:
|
||||
* Download Android File Transfer here: https://www.android.com/filetransfer/
|
||||
* To run the app from VSCode terminal:
|
||||
* On web, run `flutter run -d chrome –hot`
|
||||
* On mobile device or simulator, run `flutter run –hot -d <DEVICE_NAME>`
|
||||
|
||||

|
||||
|
||||
[FluffyChat](https://fluffychat.im) is an open source, nonprofit and cute [[matrix](https://matrix.org)] client written in [Flutter](https://flutter.dev). The goal of the app is to create an easy to use instant messenger which is open source and accessible for everyone.
|
||||
|
|
|
|||
|
|
@ -2123,9 +2123,13 @@
|
|||
"placeholders": {}
|
||||
},
|
||||
"writeAMessage": "Write a message…",
|
||||
"@writeAMessage": {
|
||||
"writeAMessageFlag": "Write a message in {l1flag} or {l2flag}…",
|
||||
"@writeAMessageFlag": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
"placeholders": {
|
||||
"l1flag": {},
|
||||
"l2flag": {}
|
||||
}
|
||||
},
|
||||
"yes": "Yes",
|
||||
"@yes": {
|
||||
|
|
@ -3989,5 +3993,6 @@
|
|||
"unread": {}
|
||||
}
|
||||
},
|
||||
"messageAnalytics": "Message Analytics"
|
||||
"messageAnalytics": "Message Analytics",
|
||||
"noPaymentInfo": "No payment info necessary!"
|
||||
}
|
||||
|
|
@ -27,6 +27,11 @@ class ChatInputRow extends StatelessWidget {
|
|||
const height = 48.0;
|
||||
|
||||
// #Pangea
|
||||
final activel1 =
|
||||
controller.pangeaController.languageController.activeL1Model();
|
||||
final activel2 =
|
||||
controller.pangeaController.languageController.activeL2Model();
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
ITBar(
|
||||
|
|
@ -325,7 +330,16 @@ class ChatInputRow extends StatelessWidget {
|
|||
bottom: 6.0,
|
||||
top: 3.0,
|
||||
),
|
||||
hintText: L10n.of(context)!.writeAMessage,
|
||||
hintText: activel1 != null && activel2 != null
|
||||
? L10n.of(context)!.writeAMessageFlag(
|
||||
activel1.languageEmoji ??
|
||||
activel1.getDisplayName(context) ??
|
||||
activel1.langCode,
|
||||
activel2.languageEmoji ??
|
||||
activel2.getDisplayName(context) ??
|
||||
activel2.langCode,
|
||||
)
|
||||
: L10n.of(context)!.writeAMessage,
|
||||
hintMaxLines: 1,
|
||||
border: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
|
|
|
|||
|
|
@ -297,22 +297,21 @@ class ClientChooserButton extends StatelessWidget {
|
|||
// onKeysPressed: () => _previousAccount(matrix, context),
|
||||
// child: const SizedBox.shrink(),
|
||||
// ),
|
||||
// Pangea#
|
||||
PopupMenuButton<Object>(
|
||||
onSelected: (o) => _clientSelected(o, context),
|
||||
itemBuilder: _bundleMenuItems,
|
||||
// #Pangea
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Material(
|
||||
borderRadius: const BorderRadius.only(
|
||||
bottomLeft: Radius.circular(12),
|
||||
bottomRight: Radius.circular(12),
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: ListTile(
|
||||
tileColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
hoverColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
leading: const Icon(Icons.settings_outlined),
|
||||
title: Text(L10n.of(context)!.mainMenu),
|
||||
color: Colors.transparent,
|
||||
child:
|
||||
// Pangea#
|
||||
PopupMenuButton<Object>(
|
||||
onSelected: (o) => _clientSelected(o, context),
|
||||
itemBuilder: _bundleMenuItems,
|
||||
// #Pangea
|
||||
child: ListTile(
|
||||
mouseCursor: SystemMouseCursors.click,
|
||||
leading: const Icon(Icons.settings_outlined),
|
||||
title: Text(L10n.of(context)!.mainMenu),
|
||||
),
|
||||
),
|
||||
),
|
||||
// child: Material(
|
||||
|
|
|
|||
|
|
@ -203,8 +203,9 @@ class NewSpaceController extends State<NewSpace> {
|
|||
final newChatRoomId = await Matrix.of(context).client.createGroupChat(
|
||||
enableEncryption: false,
|
||||
preset: sdk.CreateRoomPreset.publicChat,
|
||||
// Welcome chat name is '[space name acronym]: Welcome Chat'
|
||||
groupName:
|
||||
'${nameController.text.trim()}: ${L10n.of(context)!.classWelcomeChat}',
|
||||
'${nameController.text.trim().split(RegExp(r"\s+")).map((s) => s[0]).join()}: ${L10n.of(context)!.classWelcomeChat}',
|
||||
);
|
||||
GoogleAnalytics.createChat(newChatRoomId);
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ class Choreographer {
|
|||
OverlayUtil.showPositionedCard(
|
||||
context: context,
|
||||
cardToShow: const PaywallCard(),
|
||||
cardSize: const Size(325, 375),
|
||||
cardSize: const Size(325, 325),
|
||||
transformTargetId: inputTransformTargetKey,
|
||||
);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
class PLocalKey {
|
||||
static const String user = 'user';
|
||||
static const String matrixProfile = 'matrixProfile';
|
||||
|
||||
static const String classes = 'classes';
|
||||
|
||||
|
|
|
|||
|
|
@ -49,12 +49,14 @@ class ClassController extends BaseController {
|
|||
final String? classCode = _pangeaController.pStoreService.read(
|
||||
PLocalKey.cachedClassCodeToJoin,
|
||||
addClientIdToKey: false,
|
||||
local: true,
|
||||
);
|
||||
|
||||
if (classCode != null) {
|
||||
_pangeaController.pStoreService.delete(
|
||||
await _pangeaController.pStoreService.delete(
|
||||
PLocalKey.cachedClassCodeToJoin,
|
||||
addClientIdToKey: false,
|
||||
local: true,
|
||||
);
|
||||
await joinClasswithCode(
|
||||
context,
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/constants/language_keys.dart';
|
||||
import 'package:fluffychat/pangea/controllers/language_list_controller.dart';
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/models/class_model.dart';
|
||||
import 'package:fluffychat/pangea/models/language_model.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
import '../widgets/user_settings/p_language_dialog.dart';
|
||||
|
||||
class LanguageController {
|
||||
|
|
@ -31,16 +30,19 @@ class LanguageController {
|
|||
);
|
||||
return;
|
||||
}
|
||||
if (_userL1Code == null ||
|
||||
_userL2Code == null ||
|
||||
_userL1Code!.isEmpty ||
|
||||
_userL2Code!.isEmpty ||
|
||||
_userL1Code == LanguageKeys.unknownLanguage ||
|
||||
_userL2Code == LanguageKeys.unknownLanguage) {
|
||||
if (!languagesSet) {
|
||||
pLanguageDialog(dialogContext, callback);
|
||||
}
|
||||
}
|
||||
|
||||
bool get languagesSet =>
|
||||
_userL1Code != null &&
|
||||
_userL2Code != null &&
|
||||
_userL1Code!.isNotEmpty &&
|
||||
_userL2Code!.isNotEmpty &&
|
||||
_userL1Code != LanguageKeys.unknownLanguage &&
|
||||
_userL2Code != LanguageKeys.unknownLanguage;
|
||||
|
||||
String? get _userL1Code {
|
||||
final source =
|
||||
_pangeaController.userController.userModel?.profile?.sourceLanguage;
|
||||
|
|
|
|||
|
|
@ -33,8 +33,10 @@ class AnalyticsController extends BaseController {
|
|||
|
||||
TimeSpan get currentAnalyticsTimeSpan {
|
||||
try {
|
||||
final String? str =
|
||||
_pangeaController.pStoreService.read(_analyticsTimeSpanKey);
|
||||
final String? str = _pangeaController.pStoreService.read(
|
||||
_analyticsTimeSpanKey,
|
||||
local: true,
|
||||
);
|
||||
return str != null
|
||||
? TimeSpan.values.firstWhere((e) {
|
||||
final spanString = e.toString();
|
||||
|
|
@ -48,8 +50,11 @@ class AnalyticsController extends BaseController {
|
|||
}
|
||||
|
||||
Future<void> setCurrentAnalyticsTimeSpan(TimeSpan timeSpan) async {
|
||||
await _pangeaController.pStoreService
|
||||
.save(_analyticsTimeSpanKey, timeSpan.toString());
|
||||
await _pangeaController.pStoreService.save(
|
||||
_analyticsTimeSpanKey,
|
||||
timeSpan.toString(),
|
||||
local: true,
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<ChartAnalyticsModel?>> allClassAnalytics() async {
|
||||
|
|
|
|||
|
|
@ -136,7 +136,12 @@ class PangeaController {
|
|||
_logOutfromPangea();
|
||||
}
|
||||
Sentry.configureScope(
|
||||
(scope) => scope.setUser(SentryUser(id: matrixState.client.userID)),
|
||||
(scope) => scope.setUser(
|
||||
SentryUser(
|
||||
id: matrixState.client.userID,
|
||||
name: matrixState.client.userID,
|
||||
),
|
||||
),
|
||||
);
|
||||
GoogleAnalytics.analyticsUserUpdate(matrixState.client.userID);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:fluffychat/pangea/controllers/base_controller.dart';
|
|||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/models/class_model.dart';
|
||||
import 'package:fluffychat/pangea/models/user_model.dart';
|
||||
import 'package:fluffychat/pangea/utils/p_extension.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
|
|
@ -31,7 +32,9 @@ class PermissionsController extends BaseController {
|
|||
|
||||
/// Returns false if user is null
|
||||
bool isUser18() {
|
||||
final dob = _pangeaController.userController.matrixProfile?.dateOfBirth;
|
||||
final dob = _pangeaController.pStoreService.read(
|
||||
MatrixProfile.dateOfBirth.title,
|
||||
);
|
||||
return dob != null
|
||||
? DateTime.parse(dob).isAtLeastYearsOld(AgeLimits.toAccessFeatures)
|
||||
: false;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ 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';
|
||||
import 'package:fluffychat/pangea/models/mobile_subscriptions.dart';
|
||||
import 'package:fluffychat/pangea/models/user_model.dart';
|
||||
import 'package:fluffychat/pangea/models/web_subscriptions.dart';
|
||||
import 'package:fluffychat/pangea/network/requests.dart';
|
||||
import 'package:fluffychat/pangea/network/urls.dart';
|
||||
|
|
@ -95,9 +96,13 @@ class SubscriptionController extends BaseController {
|
|||
} else {
|
||||
final bool? beganWebPayment = _pangeaController.pStoreService.read(
|
||||
PLocalKey.beganWebPayment,
|
||||
local: true,
|
||||
);
|
||||
if (beganWebPayment ?? false) {
|
||||
_pangeaController.pStoreService.delete(PLocalKey.beganWebPayment);
|
||||
await _pangeaController.pStoreService.delete(
|
||||
PLocalKey.beganWebPayment,
|
||||
local: true,
|
||||
);
|
||||
if (_pangeaController.subscriptionController.isSubscribed) {
|
||||
subscriptionStream.add(true);
|
||||
}
|
||||
|
|
@ -133,9 +138,10 @@ class SubscriptionController extends BaseController {
|
|||
selectedSubscription.duration!,
|
||||
isPromo: isPromo,
|
||||
);
|
||||
_pangeaController.pStoreService.save(
|
||||
await _pangeaController.pStoreService.save(
|
||||
PLocalKey.beganWebPayment,
|
||||
true,
|
||||
local: true,
|
||||
);
|
||||
setState();
|
||||
launchUrlString(
|
||||
|
|
@ -177,12 +183,18 @@ class SubscriptionController extends BaseController {
|
|||
|
||||
bool get _activatedNewUserTrial =>
|
||||
_pangeaController.userController.inTrialWindow &&
|
||||
(_pangeaController.pStoreService.read(PLocalKey.activatedTrialKey) ??
|
||||
(_pangeaController.pStoreService.read(
|
||||
MatrixProfile.activatedFreeTrial.title,
|
||||
) ??
|
||||
false);
|
||||
|
||||
void activateNewUserTrial() {
|
||||
_pangeaController.pStoreService.save(PLocalKey.activatedTrialKey, true);
|
||||
setNewUserTrial();
|
||||
_pangeaController.pStoreService
|
||||
.save(
|
||||
MatrixProfile.activatedFreeTrial.title,
|
||||
true,
|
||||
)
|
||||
.then((_) => setNewUserTrial());
|
||||
}
|
||||
|
||||
void setNewUserTrial() {
|
||||
|
|
@ -226,6 +238,7 @@ class SubscriptionController extends BaseController {
|
|||
DateTime? get _lastDismissedPaywall {
|
||||
final lastDismissed = _pangeaController.pStoreService.read(
|
||||
PLocalKey.dismissedPaywall,
|
||||
local: true,
|
||||
);
|
||||
if (lastDismissed == null) return null;
|
||||
return DateTime.tryParse(lastDismissed);
|
||||
|
|
@ -234,6 +247,7 @@ class SubscriptionController extends BaseController {
|
|||
int? get _paywallBackoff {
|
||||
final backoff = _pangeaController.pStoreService.read(
|
||||
PLocalKey.paywallBackoff,
|
||||
local: true,
|
||||
);
|
||||
if (backoff == null) return null;
|
||||
return backoff;
|
||||
|
|
@ -247,18 +261,24 @@ class SubscriptionController extends BaseController {
|
|||
(24 * (_paywallBackoff ?? 1)));
|
||||
}
|
||||
|
||||
void dismissPaywall() {
|
||||
_pangeaController.pStoreService.save(
|
||||
void dismissPaywall() async {
|
||||
await _pangeaController.pStoreService.save(
|
||||
PLocalKey.dismissedPaywall,
|
||||
DateTime.now().toString(),
|
||||
local: true,
|
||||
);
|
||||
|
||||
if (_paywallBackoff == null) {
|
||||
_pangeaController.pStoreService.save(PLocalKey.paywallBackoff, 1);
|
||||
await _pangeaController.pStoreService.save(
|
||||
PLocalKey.paywallBackoff,
|
||||
1,
|
||||
local: true,
|
||||
);
|
||||
} else {
|
||||
_pangeaController.pStoreService.save(
|
||||
await _pangeaController.pStoreService.save(
|
||||
PLocalKey.paywallBackoff,
|
||||
_paywallBackoff! + 1,
|
||||
local: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fluffychat/pangea/constants/language_keys.dart';
|
||||
import 'package:fluffychat/pangea/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/constants/pangea_event_types.dart';
|
||||
import 'package:fluffychat/pangea/controllers/base_controller.dart';
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/widgets/fluffy_chat_app.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:jwt_decode/jwt_decode.dart';
|
||||
import 'package:matrix/matrix.dart' as matrix;
|
||||
|
||||
|
|
@ -23,6 +20,17 @@ class UserController extends BaseController {
|
|||
_pangeaController = pangeaController;
|
||||
}
|
||||
|
||||
Future<void> createPangeaUser({required String dob}) async {
|
||||
final PUserModel newUserModel = await PUserRepo.repoCreatePangeaUser(
|
||||
userID: userId!,
|
||||
fullName: fullname,
|
||||
dob: dob,
|
||||
matrixAccessToken: _matrixAccessToken!,
|
||||
);
|
||||
newUserModel.save(_pangeaController);
|
||||
await updateMatrixProfile(dateOfBirth: dob);
|
||||
}
|
||||
|
||||
Future<PUserModel?> fetchUserModel() async {
|
||||
try {
|
||||
if (_matrixAccessToken == null) {
|
||||
|
|
@ -30,48 +38,301 @@ class UserController extends BaseController {
|
|||
"calling fetchUserModel with matrixAccesstoken == null",
|
||||
);
|
||||
}
|
||||
|
||||
final PUserModel? newUserModel = await PUserRepo.fetchPangeaUserInfo(
|
||||
userID: userId!,
|
||||
matrixAccessToken: _matrixAccessToken!,
|
||||
);
|
||||
|
||||
if (newUserModel != null) {
|
||||
_savePUserModel(newUserModel);
|
||||
if (newUserModel.profile!.dateOfBirth != null) {
|
||||
await setMatrixProfile(newUserModel.profile!.dateOfBirth!);
|
||||
}
|
||||
final MatrixProfile? matrixProfile = await getMatrixProfile();
|
||||
_saveMatrixProfile(matrixProfile);
|
||||
}
|
||||
newUserModel?.save(_pangeaController);
|
||||
await migrateMatrixProfile();
|
||||
_completeCompleter();
|
||||
|
||||
return newUserModel;
|
||||
} catch (err) {
|
||||
log("User model not found. Probably first signup and needs Pangea account");
|
||||
debugPrint(
|
||||
"User model not found. Probably first signup and needs Pangea account",
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setMatrixProfile(String dob) async {
|
||||
await _pangeaController.matrixState.client.setAccountData(
|
||||
userId!,
|
||||
PangeaEventTypes.userAge,
|
||||
{ModelKey.userDateOfBirth: dob},
|
||||
dynamic migratedProfileInfo(MatrixProfile key) {
|
||||
final dynamic localValue = _pangeaController.pStoreService.read(
|
||||
key.title,
|
||||
local: true,
|
||||
);
|
||||
final MatrixProfile? matrixProfile = await getMatrixProfile();
|
||||
_saveMatrixProfile(matrixProfile);
|
||||
final dynamic matrixValue = _pangeaController.pStoreService.read(
|
||||
key.title,
|
||||
);
|
||||
return localValue != null && matrixValue != localValue ? localValue : null;
|
||||
}
|
||||
|
||||
Future<MatrixProfile?> getMatrixProfile() async {
|
||||
try {
|
||||
final Map<String, dynamic> accountData =
|
||||
await _pangeaController.matrixState.client.getAccountData(
|
||||
userId!,
|
||||
PangeaEventTypes.userAge,
|
||||
Future<void> migrateMatrixProfile() async {
|
||||
final Profile? pangeaProfile = userModel?.profile;
|
||||
|
||||
final String? pangeaDob = pangeaProfile?.dateOfBirth;
|
||||
final String? matrixDob = _pangeaController.pStoreService.read(
|
||||
ModelKey.userDateOfBirth,
|
||||
);
|
||||
final String? dob =
|
||||
pangeaDob != null && matrixDob != pangeaDob ? pangeaDob : null;
|
||||
|
||||
final pangeaCreatedAt = pangeaProfile?.createdAt;
|
||||
final matrixCreatedAt = _pangeaController.pStoreService.read(
|
||||
MatrixProfile.createdAt.title,
|
||||
);
|
||||
final String? createdAt =
|
||||
pangeaCreatedAt != null && matrixCreatedAt != pangeaCreatedAt
|
||||
? pangeaCreatedAt
|
||||
: null;
|
||||
|
||||
final String? pangeaTargetLanguage = pangeaProfile?.targetLanguage;
|
||||
final String? matrixTargetLanguage = _pangeaController.pStoreService.read(
|
||||
MatrixProfile.targetLanguage.title,
|
||||
);
|
||||
final String? targetLanguage = pangeaTargetLanguage != null &&
|
||||
matrixTargetLanguage != pangeaTargetLanguage
|
||||
? pangeaTargetLanguage
|
||||
: null;
|
||||
|
||||
final String? pangeaSourceLanguage = pangeaProfile?.sourceLanguage;
|
||||
final String? matrixSourceLanguage = _pangeaController.pStoreService.read(
|
||||
MatrixProfile.sourceLanguage.title,
|
||||
);
|
||||
final String? sourceLanguage = pangeaSourceLanguage != null &&
|
||||
matrixSourceLanguage != pangeaSourceLanguage
|
||||
? pangeaSourceLanguage
|
||||
: null;
|
||||
|
||||
final String? pangeaCountry = pangeaProfile?.country;
|
||||
final String? matrixCountry = _pangeaController.pStoreService.read(
|
||||
MatrixProfile.country.title,
|
||||
);
|
||||
final String? country =
|
||||
pangeaCountry != null && matrixCountry != pangeaCountry
|
||||
? pangeaCountry
|
||||
: null;
|
||||
|
||||
final bool? pangeaPublicProfile = pangeaProfile?.publicProfile;
|
||||
final bool? matrixPublicProfile = _pangeaController.pStoreService.read(
|
||||
MatrixProfile.publicProfile.title,
|
||||
);
|
||||
final bool? publicProfile = pangeaPublicProfile != null &&
|
||||
matrixPublicProfile != pangeaPublicProfile
|
||||
? pangeaPublicProfile
|
||||
: null;
|
||||
|
||||
final bool? autoPlay = migratedProfileInfo(MatrixProfile.autoPlayMessages);
|
||||
final bool? trial = migratedProfileInfo(MatrixProfile.activatedFreeTrial);
|
||||
final bool? interactiveTranslator =
|
||||
migratedProfileInfo(MatrixProfile.interactiveTranslator);
|
||||
final bool? interactiveGrammar =
|
||||
migratedProfileInfo(MatrixProfile.interactiveGrammar);
|
||||
final bool? immersionMode =
|
||||
migratedProfileInfo(MatrixProfile.immersionMode);
|
||||
final bool? definitions = migratedProfileInfo(MatrixProfile.definitions);
|
||||
final bool? translations = migratedProfileInfo(MatrixProfile.translations);
|
||||
final bool? showItInstructions =
|
||||
migratedProfileInfo(MatrixProfile.showedItInstructions);
|
||||
final bool? showClickMessage =
|
||||
migratedProfileInfo(MatrixProfile.showedClickMessage);
|
||||
final bool? showBlurMeansTranslate =
|
||||
migratedProfileInfo(MatrixProfile.showedBlurMeansTranslate);
|
||||
|
||||
await updateMatrixProfile(
|
||||
dateOfBirth: dob,
|
||||
autoPlayMessages: autoPlay,
|
||||
activatedFreeTrial: trial,
|
||||
interactiveTranslator: interactiveTranslator,
|
||||
interactiveGrammar: interactiveGrammar,
|
||||
immersionMode: immersionMode,
|
||||
definitions: definitions,
|
||||
translations: translations,
|
||||
showedItInstructions: showItInstructions,
|
||||
showedClickMessage: showClickMessage,
|
||||
showedBlurMeansTranslate: showBlurMeansTranslate,
|
||||
createdAt: createdAt,
|
||||
targetLanguage: targetLanguage,
|
||||
sourceLanguage: sourceLanguage,
|
||||
country: country,
|
||||
publicProfile: publicProfile,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> updateUserProfile({
|
||||
String? dateOfBirth,
|
||||
String? targetLanguage,
|
||||
String? sourceLanguage,
|
||||
String? country,
|
||||
List<String>? interests,
|
||||
List<String>? speaks,
|
||||
bool? publicProfile,
|
||||
}) async {
|
||||
if (userModel == null) throw Exception("Local userModel not defined");
|
||||
final profileJson = userModel!.profile!.toJson();
|
||||
|
||||
if (dateOfBirth != null) {
|
||||
profileJson[ModelKey.userDateOfBirth] = dateOfBirth;
|
||||
}
|
||||
if (targetLanguage != null) {
|
||||
profileJson[ModelKey.userTargetLanguage] = targetLanguage;
|
||||
}
|
||||
if (sourceLanguage != null) {
|
||||
profileJson[ModelKey.userSourceLanguage] = sourceLanguage;
|
||||
}
|
||||
if (interests != null) {
|
||||
profileJson[ModelKey.userInterests] = interests.toString();
|
||||
}
|
||||
if (speaks != null) {
|
||||
profileJson[ModelKey.userSpeaks] = speaks.toString();
|
||||
}
|
||||
if (country != null) {
|
||||
profileJson[ModelKey.userCountry] = country;
|
||||
}
|
||||
if (publicProfile != null) {
|
||||
profileJson[ModelKey.publicProfile] = publicProfile;
|
||||
}
|
||||
final Profile updatedUserProfile = await PUserRepo.updateUserProfile(
|
||||
Profile.fromJson(profileJson),
|
||||
await accessToken,
|
||||
);
|
||||
|
||||
PUserModel(
|
||||
access: await accessToken,
|
||||
refresh: userModel!.refresh,
|
||||
profile: updatedUserProfile,
|
||||
).save(_pangeaController);
|
||||
|
||||
await updateMatrixProfile(
|
||||
dateOfBirth: dateOfBirth,
|
||||
targetLanguage: targetLanguage,
|
||||
sourceLanguage: sourceLanguage,
|
||||
country: country,
|
||||
publicProfile: publicProfile,
|
||||
);
|
||||
}
|
||||
|
||||
PUserModel? get userModel {
|
||||
final data = _pangeaController.pStoreService.read(
|
||||
PLocalKey.user,
|
||||
local: true,
|
||||
);
|
||||
return data != null ? PUserModel.fromJson(data) : null;
|
||||
}
|
||||
|
||||
Future<void> updateMatrixProfile({
|
||||
String? dateOfBirth,
|
||||
bool? autoPlayMessages,
|
||||
bool? activatedFreeTrial,
|
||||
bool? interactiveTranslator,
|
||||
bool? interactiveGrammar,
|
||||
bool? immersionMode,
|
||||
bool? definitions,
|
||||
bool? translations,
|
||||
bool? showedItInstructions,
|
||||
bool? showedClickMessage,
|
||||
bool? showedBlurMeansTranslate,
|
||||
String? createdAt,
|
||||
String? targetLanguage,
|
||||
String? sourceLanguage,
|
||||
String? country,
|
||||
bool? publicProfile,
|
||||
}) async {
|
||||
if (dateOfBirth != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.dateOfBirth.title,
|
||||
dateOfBirth,
|
||||
);
|
||||
}
|
||||
if (autoPlayMessages != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.autoPlayMessages.title,
|
||||
autoPlayMessages,
|
||||
);
|
||||
}
|
||||
if (activatedFreeTrial != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.activatedFreeTrial.title,
|
||||
activatedFreeTrial,
|
||||
);
|
||||
}
|
||||
if (interactiveTranslator != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.interactiveTranslator.title,
|
||||
interactiveTranslator,
|
||||
);
|
||||
}
|
||||
if (interactiveGrammar != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.interactiveGrammar.title,
|
||||
interactiveGrammar,
|
||||
);
|
||||
}
|
||||
if (immersionMode != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.immersionMode.title,
|
||||
immersionMode,
|
||||
);
|
||||
}
|
||||
if (definitions != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.definitions.title,
|
||||
definitions,
|
||||
);
|
||||
}
|
||||
if (translations != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.translations.title,
|
||||
translations,
|
||||
);
|
||||
}
|
||||
if (showedItInstructions != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.showedItInstructions.title,
|
||||
showedItInstructions,
|
||||
);
|
||||
}
|
||||
if (showedClickMessage != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.showedClickMessage.title,
|
||||
showedClickMessage,
|
||||
);
|
||||
}
|
||||
if (showedBlurMeansTranslate != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.showedBlurMeansTranslate.title,
|
||||
showedBlurMeansTranslate,
|
||||
);
|
||||
}
|
||||
if (createdAt != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.createdAt.title,
|
||||
createdAt,
|
||||
);
|
||||
}
|
||||
if (targetLanguage != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.targetLanguage.title,
|
||||
targetLanguage,
|
||||
);
|
||||
}
|
||||
if (sourceLanguage != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.sourceLanguage.title,
|
||||
sourceLanguage,
|
||||
);
|
||||
}
|
||||
if (country != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.country.title,
|
||||
country,
|
||||
);
|
||||
}
|
||||
if (publicProfile != null) {
|
||||
await _pangeaController.pStoreService.save(
|
||||
MatrixProfile.publicProfile.title,
|
||||
publicProfile,
|
||||
);
|
||||
return MatrixProfile.fromJson(accountData);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -116,16 +377,6 @@ class UserController extends BaseController {
|
|||
return userID.substring(0, userID.indexOf(":")).replaceAll("@", "");
|
||||
}
|
||||
|
||||
PUserModel? get userModel {
|
||||
final data = _pangeaController.pStoreService.read(PLocalKey.user);
|
||||
return data != null ? PUserModel.fromJson(data) : null;
|
||||
}
|
||||
|
||||
MatrixProfile? get matrixProfile {
|
||||
final data = _pangeaController.pStoreService.read(PLocalKey.matrixProfile);
|
||||
return data != null ? MatrixProfile.fromJson(data) : null;
|
||||
}
|
||||
|
||||
Future<bool> get isPUserDataAvailable async {
|
||||
try {
|
||||
final PUserModel? toCheck = userModel ?? (await fetchUserModel());
|
||||
|
|
@ -137,10 +388,15 @@ class UserController extends BaseController {
|
|||
|
||||
Future<bool> get isUserDataAvailableAndDateOfBirthSet async {
|
||||
try {
|
||||
if (matrixProfile == null) {
|
||||
await fetchUserModel();
|
||||
final client = _pangeaController.matrixState.client;
|
||||
if (client.prevBatch == null) {
|
||||
await client.onSync.stream.first;
|
||||
}
|
||||
return matrixProfile?.dateOfBirth != null ? true : false;
|
||||
await fetchUserModel();
|
||||
final localAccountData = _pangeaController.pStoreService.read(
|
||||
ModelKey.userDateOfBirth,
|
||||
);
|
||||
return localAccountData != null;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -175,99 +431,6 @@ class UserController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
redirectToUserInfo() {
|
||||
// _pangeaController.matrix.router!.currentState!.to(
|
||||
// "/home/connect/user_age",
|
||||
// queryParameters:
|
||||
// _pangeaController.matrix.router!.currentState!.queryParameters,
|
||||
// );
|
||||
FluffyChatApp.router.go("/rooms/user_age");
|
||||
}
|
||||
|
||||
_saveMatrixProfile(MatrixProfile? matrixProfile) {
|
||||
if (matrixProfile != null) {
|
||||
_pangeaController.pStoreService.save(
|
||||
PLocalKey.matrixProfile,
|
||||
matrixProfile.toJson(),
|
||||
);
|
||||
setState(data: matrixProfile);
|
||||
}
|
||||
}
|
||||
|
||||
_savePUserModel(PUserModel? pUserModel) {
|
||||
if (pUserModel == null) {
|
||||
ErrorHandler.logError(e: "trying to save null userModel");
|
||||
return;
|
||||
}
|
||||
final jsonUser = pUserModel.toJson();
|
||||
_pangeaController.pStoreService.save(PLocalKey.user, jsonUser);
|
||||
|
||||
setState(data: pUserModel);
|
||||
}
|
||||
|
||||
Future<void> updateUserProfile({
|
||||
String? dateOfBirth,
|
||||
String? targetLanguage,
|
||||
String? sourceLanguage,
|
||||
String? country,
|
||||
List<String>? interests,
|
||||
List<String>? speaks,
|
||||
bool? publicProfile,
|
||||
}) async {
|
||||
if (userModel == null) throw Exception("Local userModel not defined");
|
||||
final profileJson = userModel!.profile!.toJson();
|
||||
|
||||
if (dateOfBirth != null) {
|
||||
profileJson[ModelKey.userDateOfBirth] = dateOfBirth;
|
||||
}
|
||||
if (targetLanguage != null) {
|
||||
profileJson[ModelKey.userTargetLanguage] = targetLanguage;
|
||||
}
|
||||
if (sourceLanguage != null) {
|
||||
profileJson[ModelKey.userSourceLanguage] = sourceLanguage;
|
||||
}
|
||||
if (interests != null) {
|
||||
profileJson[ModelKey.userInterests] = interests.toString();
|
||||
}
|
||||
if (speaks != null) {
|
||||
profileJson[ModelKey.userSpeaks] = speaks.toString();
|
||||
}
|
||||
if (country != null) {
|
||||
profileJson[ModelKey.userCountry] = country;
|
||||
}
|
||||
if (publicProfile != null) {
|
||||
profileJson[ModelKey.publicProfile] = publicProfile;
|
||||
}
|
||||
final Profile updatedUserProfile = await PUserRepo.updateUserProfile(
|
||||
Profile.fromJson(profileJson),
|
||||
await accessToken,
|
||||
);
|
||||
|
||||
await _savePUserModel(
|
||||
PUserModel(
|
||||
access: await accessToken,
|
||||
refresh: userModel!.refresh,
|
||||
profile: updatedUserProfile,
|
||||
),
|
||||
);
|
||||
|
||||
if (dateOfBirth != null) {
|
||||
await setMatrixProfile(dateOfBirth);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> createPangeaUser({required String dob}) async {
|
||||
final PUserModel newUserModel = await PUserRepo.repoCreatePangeaUser(
|
||||
userID: userId!,
|
||||
fullName: fullname,
|
||||
dob: dob,
|
||||
matrixAccessToken: _matrixAccessToken!,
|
||||
);
|
||||
await _savePUserModel(newUserModel);
|
||||
|
||||
await setMatrixProfile(dob);
|
||||
}
|
||||
|
||||
String? get _matrixAccessToken =>
|
||||
_pangeaController.matrixState.client.accessToken;
|
||||
|
||||
|
|
|
|||
|
|
@ -490,7 +490,11 @@ extension PangeaRoom on Room {
|
|||
final String migratedAnalyticsKey =
|
||||
"MIGRATED_ANALYTICS_KEY${id.localpart}";
|
||||
|
||||
if (storageService?.read(migratedAnalyticsKey) ?? false) return;
|
||||
if (storageService?.read(
|
||||
migratedAnalyticsKey,
|
||||
local: true,
|
||||
) ??
|
||||
false) return;
|
||||
|
||||
if (!isPangeaClass && !isExchange) {
|
||||
throw Exception(
|
||||
|
|
@ -522,7 +526,11 @@ extension PangeaRoom on Room {
|
|||
);
|
||||
myAnalEvent.bulkUpdate(updateMessages);
|
||||
|
||||
storageService?.save(migratedAnalyticsKey, true);
|
||||
await storageService?.save(
|
||||
migratedAnalyticsKey,
|
||||
true,
|
||||
local: true,
|
||||
);
|
||||
} catch (err, s) {
|
||||
if (kDebugMode) rethrow;
|
||||
// debugger(when: kDebugMode);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -16,10 +15,10 @@ class PAuthGaurd {
|
|||
GoRouterState state,
|
||||
) async {
|
||||
if (pController != null) {
|
||||
final bool setDob = await pController!
|
||||
.userController.isUserDataAvailableAndDateOfBirthSet;
|
||||
if (Matrix.of(context).client.isLogged()) {
|
||||
return !setDob ? '/user_age' : '/rooms';
|
||||
final bool dobIsSet = await pController!
|
||||
.userController.isUserDataAvailableAndDateOfBirthSet;
|
||||
return dobIsSet ? '/rooms' : '/user_age';
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
|
|
@ -34,13 +33,12 @@ class PAuthGaurd {
|
|||
GoRouterState state,
|
||||
) async {
|
||||
if (pController != null) {
|
||||
final bool setDob = await pController!
|
||||
if (!Matrix.of(context).client.isLogged()) {
|
||||
return '/home';
|
||||
}
|
||||
final bool dobIsSet = await pController!
|
||||
.userController.isUserDataAvailableAndDateOfBirthSet;
|
||||
return !Matrix.of(context).client.isLogged()
|
||||
? '/home'
|
||||
: !setDob
|
||||
? '/user_age'
|
||||
: null;
|
||||
return dobIsSet ? null : '/user_age';
|
||||
} else {
|
||||
debugPrint("controller is null in pguard check");
|
||||
return Matrix.of(context).client.isLogged() ? null : '/home';
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ class LanguageModel {
|
|||
final String langCode;
|
||||
final String languageFlag;
|
||||
final String displayName;
|
||||
final String? languageEmoji;
|
||||
final bool l2;
|
||||
final bool l1;
|
||||
|
||||
|
|
@ -20,6 +21,7 @@ class LanguageModel {
|
|||
required this.displayName,
|
||||
required this.l2,
|
||||
required this.l1,
|
||||
this.languageEmoji,
|
||||
});
|
||||
|
||||
factory LanguageModel.fromJson(json) {
|
||||
|
|
@ -37,6 +39,7 @@ class LanguageModel {
|
|||
),
|
||||
l2: json["l2"] ?? code.contains("es") || code.contains("en"),
|
||||
l1: json["l1"] ?? !code.contains("es") && !code.contains("en"),
|
||||
languageEmoji: json['language_emoji'],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -46,6 +49,7 @@ class LanguageModel {
|
|||
'language_flag': languageFlag,
|
||||
'l2': l2,
|
||||
'l1': l1,
|
||||
'language_emoji': languageEmoji,
|
||||
};
|
||||
|
||||
// Discuss with Jordan - adding langCode field to language objects as separate from displayName
|
||||
|
|
@ -81,6 +85,7 @@ class LanguageModel {
|
|||
l1: false,
|
||||
langCode: LanguageKeys.multiLanguage,
|
||||
languageFlag: 'assets/colors.png',
|
||||
languageEmoji: "🌎",
|
||||
);
|
||||
|
||||
// Discuss with Jordan
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:country_picker/country_picker.dart';
|
||||
import 'package:fluffychat/pangea/constants/local.key.dart';
|
||||
import 'package:fluffychat/pangea/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/models/class_model.dart';
|
||||
import 'package:fluffychat/pangea/utils/instructions.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
|
|
@ -37,23 +41,71 @@ class PUserModel {
|
|||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
Future<void> save(PangeaController pangeaController) async {
|
||||
await pangeaController.pStoreService.save(
|
||||
PLocalKey.user,
|
||||
toJson(),
|
||||
local: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MatrixProfile {
|
||||
String dateOfBirth;
|
||||
enum MatrixProfile {
|
||||
dateOfBirth,
|
||||
autoPlayMessages,
|
||||
activatedFreeTrial,
|
||||
interactiveTranslator,
|
||||
interactiveGrammar,
|
||||
immersionMode,
|
||||
definitions,
|
||||
translations,
|
||||
showedItInstructions,
|
||||
showedClickMessage,
|
||||
showedBlurMeansTranslate,
|
||||
createdAt,
|
||||
targetLanguage,
|
||||
sourceLanguage,
|
||||
country,
|
||||
publicProfile,
|
||||
}
|
||||
|
||||
MatrixProfile({
|
||||
required this.dateOfBirth,
|
||||
});
|
||||
|
||||
factory MatrixProfile.fromJson(Map<String, dynamic> json) => MatrixProfile(
|
||||
dateOfBirth: json[ModelKey.userDateOfBirth],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data[ModelKey.userDateOfBirth] = dateOfBirth;
|
||||
return data;
|
||||
extension MatrixProfileExtension on MatrixProfile {
|
||||
String get title {
|
||||
switch (this) {
|
||||
case MatrixProfile.dateOfBirth:
|
||||
return ModelKey.userDateOfBirth;
|
||||
case MatrixProfile.autoPlayMessages:
|
||||
return PLocalKey.autoPlayMessages;
|
||||
case MatrixProfile.activatedFreeTrial:
|
||||
return PLocalKey.activatedTrialKey;
|
||||
case MatrixProfile.interactiveTranslator:
|
||||
return ToolSetting.interactiveTranslator.toString();
|
||||
case MatrixProfile.interactiveGrammar:
|
||||
return ToolSetting.interactiveGrammar.toString();
|
||||
case MatrixProfile.immersionMode:
|
||||
return ToolSetting.immersionMode.toString();
|
||||
case MatrixProfile.definitions:
|
||||
return ToolSetting.definitions.toString();
|
||||
case MatrixProfile.translations:
|
||||
return ToolSetting.translations.toString();
|
||||
case MatrixProfile.showedItInstructions:
|
||||
return InstructionsEnum.itInstructions.toString();
|
||||
case MatrixProfile.showedClickMessage:
|
||||
return InstructionsEnum.clickMessage.toString();
|
||||
case MatrixProfile.showedBlurMeansTranslate:
|
||||
return InstructionsEnum.blurMeansTranslate.toString();
|
||||
case MatrixProfile.createdAt:
|
||||
return ModelKey.userCreatedAt;
|
||||
case MatrixProfile.targetLanguage:
|
||||
return ModelKey.l2LanguageKey;
|
||||
case MatrixProfile.sourceLanguage:
|
||||
return ModelKey.l1LanguageKey;
|
||||
case MatrixProfile.country:
|
||||
return ModelKey.userCountry;
|
||||
case MatrixProfile.publicProfile:
|
||||
return ModelKey.publicProfile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ class SettingsLearningView extends StatelessWidget {
|
|||
title: setting.toolName(context),
|
||||
subtitle: setting.toolDescription(context),
|
||||
pStoreKey: setting.toString(),
|
||||
local: false,
|
||||
),
|
||||
PSettingsSwitchListTile.adaptive(
|
||||
defaultValue: controller.pangeaController.pStoreService.read(
|
||||
|
|
@ -68,6 +69,7 @@ class SettingsLearningView extends StatelessWidget {
|
|||
title: L10n.of(context)!.autoPlayTitle,
|
||||
subtitle: L10n.of(context)!.autoPlayDesc,
|
||||
pStoreKey: PLocalKey.autoPlayMessages,
|
||||
local: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -25,8 +25,14 @@ class InstructionsController {
|
|||
_instructionsClosed[key] ??
|
||||
false;
|
||||
|
||||
void updateEnableInstructions(InstructionsEnum key, bool value) =>
|
||||
_pangeaController.pStoreService.save(key.toString(), value);
|
||||
Future<void> updateEnableInstructions(
|
||||
InstructionsEnum key,
|
||||
bool value,
|
||||
) async =>
|
||||
await _pangeaController.pStoreService.save(
|
||||
key.toString(),
|
||||
value,
|
||||
);
|
||||
|
||||
Future<void> show(
|
||||
BuildContext context,
|
||||
|
|
@ -149,9 +155,11 @@ class InstructionsToggleState extends State<InstructionsToggle> {
|
|||
title: Text(L10n.of(context)!.doNotShowAgain),
|
||||
value: pangeaController.instructions
|
||||
.wereInstructionsTurnedOff(widget.instructionsKey),
|
||||
onChanged: ((value) {
|
||||
pangeaController.instructions
|
||||
.updateEnableInstructions(widget.instructionsKey, value);
|
||||
onChanged: ((value) async {
|
||||
await pangeaController.instructions.updateEnableInstructions(
|
||||
widget.instructionsKey,
|
||||
value,
|
||||
);
|
||||
setState(() {});
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:get_storage/get_storage.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
class PLocalStore {
|
||||
final GetStorage _box = GetStorage();
|
||||
|
|
@ -13,24 +14,112 @@ class PLocalStore {
|
|||
String key,
|
||||
dynamic data, {
|
||||
bool addClientIdToKey = true,
|
||||
bool local = false,
|
||||
}) async {
|
||||
local
|
||||
? await saveLocal(
|
||||
key,
|
||||
data,
|
||||
addClientIdToKey: addClientIdToKey,
|
||||
)
|
||||
: await saveProfile(key, data);
|
||||
}
|
||||
|
||||
/// fetch data from local
|
||||
dynamic read(
|
||||
String key, {
|
||||
bool addClientIdToKey = true,
|
||||
local = false,
|
||||
}) {
|
||||
return local
|
||||
? readLocal(
|
||||
key,
|
||||
addClientIdToKey: addClientIdToKey,
|
||||
)
|
||||
: readProfile(key);
|
||||
}
|
||||
|
||||
/// delete data from local
|
||||
Future<void> delete(
|
||||
String key, {
|
||||
bool addClientIdToKey = true,
|
||||
local = false,
|
||||
}) async {
|
||||
return local
|
||||
? deleteLocal(
|
||||
key,
|
||||
addClientIdToKey: addClientIdToKey,
|
||||
)
|
||||
: deleteProfile(key);
|
||||
}
|
||||
|
||||
/// save data in local
|
||||
Future<void> saveLocal(
|
||||
String key,
|
||||
dynamic data, {
|
||||
bool addClientIdToKey = true,
|
||||
}) async {
|
||||
await _box.write(_key(key, addClientIdToKey: addClientIdToKey), data);
|
||||
}
|
||||
|
||||
Future<void> saveProfile(
|
||||
String key,
|
||||
dynamic data,
|
||||
) async {
|
||||
final waitForAccountSync =
|
||||
pangeaController.matrixState.client.onSync.stream.firstWhere(
|
||||
(sync) =>
|
||||
sync.accountData != null &&
|
||||
sync.accountData!.any(
|
||||
(event) => event.content.keys.any(
|
||||
(k) => k == key,
|
||||
),
|
||||
),
|
||||
);
|
||||
await pangeaController.matrixState.client.setAccountData(
|
||||
pangeaController.matrixState.client.userID!,
|
||||
key,
|
||||
{key: data},
|
||||
);
|
||||
await waitForAccountSync;
|
||||
await pangeaController.matrixState.client.onSyncStatus.stream.firstWhere(
|
||||
(syncStatus) => syncStatus.status == SyncStatus.finished,
|
||||
);
|
||||
}
|
||||
|
||||
/// fetch data from local
|
||||
dynamic read(String key, {bool addClientIdToKey = true}) {
|
||||
dynamic readLocal(String key, {bool addClientIdToKey = true}) {
|
||||
return pangeaController.matrixState.client.userID != null
|
||||
? _box.read(_key(key, addClientIdToKey: addClientIdToKey))
|
||||
: null;
|
||||
}
|
||||
|
||||
dynamic readProfile(String key) {
|
||||
try {
|
||||
return pangeaController.matrixState.client.accountData[key]?.content[key];
|
||||
} catch (err) {
|
||||
ErrorHandler.logError(e: err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// delete data from local
|
||||
Future<void> delete(String key, {bool addClientIdToKey = true}) async {
|
||||
Future<void> deleteLocal(String key, {bool addClientIdToKey = true}) async {
|
||||
return pangeaController.matrixState.client.userID != null
|
||||
? _box.remove(_key(key, addClientIdToKey: addClientIdToKey))
|
||||
: null;
|
||||
}
|
||||
|
||||
Future<void> deleteProfile(key) async {
|
||||
return pangeaController.matrixState.client.userID != null
|
||||
? pangeaController.matrixState.client.setAccountData(
|
||||
pangeaController.matrixState.client.userID!,
|
||||
key,
|
||||
{key: null},
|
||||
)
|
||||
: null;
|
||||
}
|
||||
|
||||
_key(String key, {bool addClientIdToKey = true}) {
|
||||
return addClientIdToKey
|
||||
? pangeaController.matrixState.client.userID! + key
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/constants/url_query_parameter_keys.dart';
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/utils/class_code.dart';
|
||||
import 'package:fluffychat/widgets/layouts/empty_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../widgets/matrix.dart';
|
||||
import '../../constants/local.key.dart';
|
||||
import '../../utils/error_handler.dart';
|
||||
|
|
@ -49,6 +48,7 @@ class _JoinClassWithLinkState extends State<JoinClassWithLink> {
|
|||
PLocalKey.cachedClassCodeToJoin,
|
||||
classCode,
|
||||
addClientIdToKey: false,
|
||||
local: true,
|
||||
);
|
||||
context.go("/home");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -37,7 +37,10 @@ class PangeaRichText extends StatefulWidget {
|
|||
class PangeaRichTextState extends State<PangeaRichText> {
|
||||
final PangeaController pangeaController = MatrixState.pangeaController;
|
||||
bool _fetchingRepresentation = false;
|
||||
double get blur => _fetchingRepresentation && widget.immersionMode ? 5 : 0;
|
||||
double get blur => (_fetchingRepresentation && widget.immersionMode) ||
|
||||
!pangeaController.languageController.languagesSet
|
||||
? 5
|
||||
: 0;
|
||||
String textSpan = "";
|
||||
PangeaRepresentation? repEvent;
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class PangeaTextController extends TextEditingController {
|
|||
OverlayUtil.showPositionedCard(
|
||||
context: context,
|
||||
cardToShow: const PaywallCard(),
|
||||
cardSize: const Size(325, 375),
|
||||
cardSize: const Size(325, 325),
|
||||
transformTargetId: choreographer.inputTransformTargetKey,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import 'package:fluffychat/pangea/widgets/igc/card_header.dart';
|
|||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
|
||||
class PaywallCard extends StatelessWidget {
|
||||
const PaywallCard({
|
||||
|
|
@ -31,13 +30,17 @@ class PaywallCard extends StatelessWidget {
|
|||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const OptionsShimmer(),
|
||||
const SizedBox(height: 15.0),
|
||||
Text(
|
||||
L10n.of(context)!.subscriptionPopupDesc,
|
||||
style: BotStyle.text(context),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (inTrialWindow)
|
||||
Text(
|
||||
L10n.of(context)!.noPaymentInfo,
|
||||
style: BotStyle.text(context),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 15.0),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
|
|
@ -88,46 +91,3 @@ class PaywallCard extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OptionsShimmer extends StatelessWidget {
|
||||
const OptionsShimmer({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Shimmer.fromColors(
|
||||
baseColor: Theme.of(context).colorScheme.primary.withOpacity(0.5),
|
||||
highlightColor: Theme.of(context).colorScheme.primary.withOpacity(0.1),
|
||||
direction: ShimmerDirection.ltr,
|
||||
child: Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
children: List.generate(
|
||||
3,
|
||||
(_) => Container(
|
||||
margin: const EdgeInsets.all(2),
|
||||
padding: EdgeInsets.zero,
|
||||
child: TextButton(
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStateProperty.all(
|
||||
const EdgeInsets.symmetric(horizontal: 7),
|
||||
),
|
||||
backgroundColor: MaterialStateProperty.all<Color>(
|
||||
Theme.of(context).colorScheme.primary.withOpacity(0.1),
|
||||
),
|
||||
shape: MaterialStateProperty.all(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: () {},
|
||||
child: Text(
|
||||
"",
|
||||
style: BotStyle.text(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PSettingsSwitchListTile extends StatefulWidget {
|
||||
final bool defaultValue;
|
||||
final String pStoreKey;
|
||||
final String title;
|
||||
final String? subtitle;
|
||||
final bool local;
|
||||
|
||||
const PSettingsSwitchListTile.adaptive({
|
||||
super.key,
|
||||
|
|
@ -16,6 +17,7 @@ class PSettingsSwitchListTile extends StatefulWidget {
|
|||
required this.pStoreKey,
|
||||
required this.title,
|
||||
this.subtitle,
|
||||
this.local = false,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -23,17 +25,41 @@ class PSettingsSwitchListTile extends StatefulWidget {
|
|||
}
|
||||
|
||||
class PSettingsSwitchListTileState extends State<PSettingsSwitchListTile> {
|
||||
bool currentValue = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
currentValue = MatrixState.pangeaController.pStoreService.read(
|
||||
widget.pStoreKey,
|
||||
local: widget.local,
|
||||
) ??
|
||||
widget.defaultValue;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final PangeaController pangeaController = MatrixState.pangeaController;
|
||||
return SwitchListTile.adaptive(
|
||||
value: pangeaController.pStoreService.read(widget.pStoreKey) ??
|
||||
widget.defaultValue,
|
||||
value: currentValue,
|
||||
title: Text(widget.title),
|
||||
activeColor: AppConfig.activeToggleColor,
|
||||
subtitle: widget.subtitle != null ? Text(widget.subtitle!) : null,
|
||||
onChanged: (bool newValue) async {
|
||||
pangeaController.pStoreService.save(widget.pStoreKey, newValue);
|
||||
try {
|
||||
await pangeaController.pStoreService.save(
|
||||
widget.pStoreKey,
|
||||
newValue,
|
||||
local: widget.local,
|
||||
);
|
||||
currentValue = newValue;
|
||||
} catch (err, s) {
|
||||
ErrorHandler.logError(
|
||||
e: err,
|
||||
m: "Failed to updates user setting ${widget.pStoreKey}",
|
||||
s: s,
|
||||
);
|
||||
}
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import 'package:intl/intl.dart';
|
|||
import 'package:matrix/encryption.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:universal_html/html.dart' as html;
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
|
@ -241,6 +242,14 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
|||
}
|
||||
initLoadingDialog();
|
||||
// #Pangea
|
||||
Sentry.configureScope(
|
||||
(scope) => scope.setUser(
|
||||
SentryUser(
|
||||
id: client.userID,
|
||||
name: client.userID,
|
||||
),
|
||||
),
|
||||
);
|
||||
pangeaController = PangeaController(matrix: widget, matrixState: this);
|
||||
// PAuthGaurd.isLogged = client.isLogged();
|
||||
// Pangea#
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue