diff --git a/lib/main.dart b/lib/main.dart index a3facf5aa..c9ab5ce21 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,12 +6,14 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:get_storage/get_storage.dart'; import 'package:matrix/matrix.dart'; +import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/common/utils/firebase_analytics.dart'; +import 'package:fluffychat/pangea/learning_settings/utils/locale_provider.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -120,7 +122,16 @@ Future startGui(List clients, SharedPreferences store) async { DeviceOrientation.portraitUp, // Lock to portrait mode ]); // Pangea# - runApp(FluffyChatApp(clients: clients, pincode: pin, store: store)); + + // #Pangea + // runApp(FluffyChatApp(clients: clients, pincode: pin, store: store)); + runApp( + ChangeNotifierProvider( + create: (_) => LocaleProvider(), + child: FluffyChatApp(clients: clients, pincode: pin, store: store), + ), + ); + // Pangea# } /// Watches the lifecycle changes to start the application when it diff --git a/lib/pangea/activity_suggestions/activity_suggestions_area.dart b/lib/pangea/activity_suggestions/activity_suggestions_area.dart index fb3162c13..8a4e8d023 100644 --- a/lib/pangea/activity_suggestions/activity_suggestions_area.dart +++ b/lib/pangea/activity_suggestions/activity_suggestions_area.dart @@ -45,15 +45,12 @@ class ActivitySuggestionsAreaState extends State { super.initState(); _setActivityItems(); - _languageStream ??= MatrixState.pangeaController.userController.stateStream + _languageStream ??= MatrixState + .pangeaController.userController.languageStream.stream .listen((update) { - if (update is Map && - (update.containsKey('prev_base_lang') || - update.containsKey('prev_target_lang'))) { - WidgetsBinding.instance.addPostFrameCallback( - (_) => _setActivityItems(), - ); - } + WidgetsBinding.instance.addPostFrameCallback( + (_) => _setActivityItems(), + ); }); } diff --git a/lib/pangea/analytics_misc/put_analytics_controller.dart b/lib/pangea/analytics_misc/put_analytics_controller.dart index d2df860a8..5d4c3bf08 100644 --- a/lib/pangea/analytics_misc/put_analytics_controller.dart +++ b/lib/pangea/analytics_misc/put_analytics_controller.dart @@ -59,12 +59,8 @@ class PutAnalyticsController extends BaseController { // Listen for changes to the user's language settings _languageStream ??= - _pangeaController.userController.stateStream.listen((update) { - if (update is Map && - update['prev_target_lang'] is LanguageModel) { - final LanguageModel previousL2 = update['prev_target_lang']; - _onUpdateLanguages(previousL2); - } + _pangeaController.userController.languageStream.stream.listen((update) { + _onUpdateLanguages(update.prevTargetLang); }); _refreshAnalyticsIfOutdated(); diff --git a/lib/pangea/analytics_summary/learning_progress_indicators.dart b/lib/pangea/analytics_summary/learning_progress_indicators.dart index 1ed526803..c6d091b9b 100644 --- a/lib/pangea/analytics_summary/learning_progress_indicators.dart +++ b/lib/pangea/analytics_summary/learning_progress_indicators.dart @@ -57,8 +57,9 @@ class LearningProgressIndicatorsState .listen(updateData); // rebuild when target language changes - _languageSubscription = - MatrixState.pangeaController.userController.stateStream.listen((_) { + _languageSubscription = MatrixState + .pangeaController.userController.languageStream.stream + .listen((_) { if (mounted) setState(() {}); }); } diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart index 3d1b7fa3d..e456bdddf 100644 --- a/lib/pangea/choreographer/controllers/choreographer.dart +++ b/lib/pangea/choreographer/controllers/choreographer.dart @@ -55,6 +55,7 @@ class Choreographer { final StreamController stateStream = StreamController.broadcast(); StreamSubscription? _languageStream; + StreamSubscription? _settingsUpdateStream; late AssistanceState _currentAssistanceState; String? translatedText; @@ -70,14 +71,13 @@ class Choreographer { errorService = ErrorService(this); _textController.addListener(_onChangeListener); _languageStream = - pangeaController.userController.stateStream.listen((update) { - if (update is Map && - update['prev_target_lang'] is LanguageModel) { - clear(); - } + pangeaController.userController.languageStream.stream.listen((update) { + clear(); + setState(); + }); - // refresh on any profile update, to account - // for changes like enabling autocorrect + _settingsUpdateStream = + pangeaController.userController.settingsUpdateStream.stream.listen((_) { setState(); }); _currentAssistanceState = assistanceState; @@ -589,6 +589,7 @@ class Choreographer { dispose() { _textController.dispose(); _languageStream?.cancel(); + _settingsUpdateStream?.cancel(); stateStream.close(); TtsController.stop(); } diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index 71ee7cb44..4fe1a747d 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -1,10 +1,11 @@ import 'dart:async'; import 'dart:math'; -import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:get_storage/get_storage.dart'; import 'package:matrix/matrix.dart'; +import 'package:provider/provider.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:fluffychat/config/app_config.dart'; @@ -17,6 +18,7 @@ import 'package:fluffychat/pangea/events/controllers/message_data_controller.dar import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/guard/p_vguard.dart'; import 'package:fluffychat/pangea/learning_settings/controllers/language_controller.dart'; +import 'package:fluffychat/pangea/learning_settings/utils/locale_provider.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; import 'package:fluffychat/pangea/spaces/controllers/space_controller.dart'; import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart'; @@ -99,10 +101,11 @@ class PangeaController { PAuthGaurd.pController = this; } - _logOutfromPangea() { + _logOutfromPangea(BuildContext context) { debugPrint("Pangea logout"); GoogleAnalytics.logout(); clearCache(); + Provider.of(context, listen: false).setLocale(null); } static final List _storageKeys = [ @@ -126,9 +129,10 @@ class PangeaController { 'onboarding_storage', ]; - Future clearCache() async { + Future clearCache({List exclude = const []}) async { final List> futures = []; for (final key in _storageKeys) { + if (exclude.contains(key)) continue; futures.add(GetStorage(key).erase()); } await Future.wait(futures); @@ -160,7 +164,11 @@ class PangeaController { } /// check user information if not found then redirect to Date of birth page - void handleLoginStateChange(LoginState state, String? userID) { + void handleLoginStateChange( + LoginState state, + String? userID, + BuildContext context, + ) { switch (state) { case LoginState.loggedOut: case LoginState.softLoggedOut: @@ -170,6 +178,7 @@ class PangeaController { userController.clear(); _languageStream?.cancel(); _languageStream = null; + _logOutfromPangea(context); break; case LoginState.loggedIn: // Initialize analytics data @@ -177,13 +186,14 @@ class PangeaController { getAnalytics.initialize(); _setLanguageStream(); - userController.reinitialize(); + userController.reinitialize().then((_) { + final l1 = userController.profile.userSettings.sourceLanguage; + Provider.of(context, listen: false).setLocale(l1); + }); subscriptionController.reinitialize(); break; } - if (state != LoginState.loggedIn) { - _logOutfromPangea(); - } + Sentry.configureScope( (scope) => scope.setUser( SentryUser( @@ -204,12 +214,9 @@ class PangeaController { void _setLanguageStream() { _languageStream?.cancel(); - _languageStream = userController.stateStream.listen((update) { - if (update is Map && - update['prev_target_lang'] != null) { - clearCache(); - } - }); + _languageStream = userController.languageStream.stream.listen( + (_) => clearCache(exclude: ["analytics_storage"]), + ); } Future setPangeaPushRules() async { diff --git a/lib/pangea/learning_settings/utils/locale_provider.dart b/lib/pangea/learning_settings/utils/locale_provider.dart new file mode 100644 index 000000000..f2c2076df --- /dev/null +++ b/lib/pangea/learning_settings/utils/locale_provider.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class LocaleProvider extends ChangeNotifier { + Locale? _locale; + + Locale? get locale => _locale; + + void setLocale(String? value) { + if (value == null || value.isEmpty) { + _locale = null; + notifyListeners(); + return; + } + + final split = value.split('-'); + _locale = Locale(split[0], split.length > 1 ? split[1] : null); + notifyListeners(); + } +} diff --git a/lib/pangea/lemmas/construct_xp_widget.dart b/lib/pangea/lemmas/construct_xp_widget.dart index 4ca5b80f2..9f2ce4720 100644 --- a/lib/pangea/lemmas/construct_xp_widget.dart +++ b/lib/pangea/lemmas/construct_xp_widget.dart @@ -34,8 +34,6 @@ class ConstructXpWidgetState extends State bool didChange = false; late AnimationController _controller; - late Animation _offsetAnimation; - late Animation _fadeAnimation; StreamSubscription? _sub; @@ -48,26 +46,6 @@ class ConstructXpWidgetState extends State vsync: this, ); - _offsetAnimation = Tween( - begin: Offset.zero, - end: const Offset(0.0, -1.0), - ).animate( - CurvedAnimation( - parent: _controller, - curve: Curves.easeOut, - ), - ); - - _fadeAnimation = Tween( - begin: 1.0, - end: 0.0, - ).animate( - CurvedAnimation( - parent: _controller, - curve: Curves.easeOut, - ), - ); - setState(() => constructLemmaCategory = constructUse?.lemmaCategory); debugPrint('constructLemmaCategory: $constructLemmaCategory'); diff --git a/lib/pangea/toolbar/widgets/message_selection_overlay.dart b/lib/pangea/toolbar/widgets/message_selection_overlay.dart index 8d07a0c8d..6d1f71bb9 100644 --- a/lib/pangea/toolbar/widgets/message_selection_overlay.dart +++ b/lib/pangea/toolbar/widgets/message_selection_overlay.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:developer'; -import 'dart:math'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -16,7 +15,6 @@ import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart'; import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart'; import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/common/utils/overlay.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_representation_event.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; @@ -286,34 +284,6 @@ class MessageOverlayController extends State // } } - void _showReadingAssistanceContent() { - if (selectedToken == null) return; - if (MatrixState.pAnyState.isOverlayOpen( - selectedToken!.text.uniqueKey, - )) { - return; - } - - final entry = ReadingAssistanceContent( - key: wordZoomKey, - pangeaMessageEvent: pangeaMessageEvent!, - overlayController: this, - ); - if (mounted) { - OverlayUtil.showPositionedCard( - context: context, - cardToShow: entry, - transformTargetId: selectedToken!.text.uniqueKey, - closePrevOverlay: false, - backDropToDismiss: false, - addBorder: false, - overlayKey: "${selectedToken!.text.uniqueKey}_toolbar", - maxHeight: AppConfig.toolbarMaxHeight, - maxWidth: min(AppConfig.toolbarMinWidth, maxWidth), - ); - } - } - void updateToolbarMode(MessageMode mode) => setState(() { selectedChoice = null; diff --git a/lib/pangea/user/controllers/user_controller.dart b/lib/pangea/user/controllers/user_controller.dart index b92596bf6..22332084e 100644 --- a/lib/pangea/user/controllers/user_controller.dart +++ b/lib/pangea/user/controllers/user_controller.dart @@ -6,7 +6,6 @@ import 'package:matrix/matrix.dart' as matrix; import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; -import 'package:fluffychat/pangea/common/controllers/base_controller.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; @@ -16,9 +15,29 @@ import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart' import 'package:fluffychat/pangea/user/models/profile_model.dart'; import '../models/user_model.dart'; +class LanguageUpdate { + final LanguageModel? prevBaseLang; + final LanguageModel? prevTargetLang; + final LanguageModel baseLang; + final LanguageModel targetLang; + + LanguageUpdate({ + required this.baseLang, + required this.targetLang, + this.prevBaseLang, + this.prevTargetLang, + }); +} + /// Controller that manages saving and reading of user/profile information -class UserController extends BaseController { +class UserController { late PangeaController _pangeaController; + + final StreamController languageStream = + StreamController.broadcast(); + final StreamController settingsUpdateStream = + StreamController.broadcast(); + UserController(PangeaController pangeaController) : super() { _pangeaController = pangeaController; } @@ -94,19 +113,19 @@ class UserController extends BaseController { await updatedProfile.saveProfileData(waitForDataInSync: waitForDataInSync); - Map? profileUpdate; - - if (prevTargetLang != _pangeaController.languageController.userL2) { - profileUpdate ??= {}; - profileUpdate['prev_target_lang'] = prevTargetLang; - } - - if (prevBaseLang != _pangeaController.languageController.userL1) { - profileUpdate ??= {}; - profileUpdate['prev_base_lang'] = prevBaseLang; + if ((prevTargetLang != _pangeaController.languageController.userL2) || + (prevBaseLang != _pangeaController.languageController.userL1)) { + languageStream.add( + LanguageUpdate( + baseLang: _pangeaController.languageController.userL1!, + targetLang: _pangeaController.languageController.userL2!, + prevBaseLang: prevBaseLang, + prevTargetLang: prevTargetLang, + ), + ); + } else { + settingsUpdateStream.add(null); } - - setState(profileUpdate); } /// A completer for the profile model of a user. @@ -131,7 +150,6 @@ class UserController extends BaseController { _pangeaController.languageController.userL2 == null) { // update the language list and send an update to refresh analytics summary await PLanguageStore.initialize(forceRefresh: true); - setState(null); } } catch (err, s) { ErrorHandler.logError( diff --git a/lib/widgets/fluffy_chat_app.dart b/lib/widgets/fluffy_chat_app.dart index 92625794b..12045d233 100644 --- a/lib/widgets/fluffy_chat_app.dart +++ b/lib/widgets/fluffy_chat_app.dart @@ -4,12 +4,14 @@ import 'package:country_picker/country_picker.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; +import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:fluffychat/config/routes.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/utils/firebase_analytics.dart'; +import 'package:fluffychat/pangea/learning_settings/utils/locale_provider.dart'; import 'package:fluffychat/widgets/app_lock.dart'; import 'package:fluffychat/widgets/theme_builder.dart'; import '../config/app_config.dart'; @@ -58,6 +60,7 @@ class FluffyChatApp extends StatelessWidget { FluffyThemes.buildTheme(context, Brightness.dark, primaryColor), scrollBehavior: CustomScrollBehavior(), // #Pangea + locale: Provider.of(context).locale, // localizationsDelegates: L10n.localizationsDelegates, localizationsDelegates: const [ L10n.delegate, diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index 068fafef0..268af46b2 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -21,6 +21,8 @@ import 'package:url_launcher/url_launcher_string.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/any_state_holder.dart'; +import 'package:fluffychat/pangea/common/utils/error_handler.dart'; +import 'package:fluffychat/pangea/learning_settings/utils/locale_provider.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -181,6 +183,7 @@ class MatrixState extends State with WidgetsBindingObserver { MatrixState.pangeaController.handleLoginStateChange( LoginState.loggedIn, _loginClientCandidate!.userID, + context, ); // Pangea# if (!widget.clients.contains(_loginClientCandidate)) { @@ -259,9 +262,38 @@ class MatrixState extends State with WidgetsBindingObserver { ), ); pangeaController = PangeaController(matrix: widget, matrixState: this); + WidgetsBinding.instance.addPostFrameCallback((_) { + _setAppLanguage(); + _setLanguageListener(); + }); // Pangea# } + // #Pangea + StreamSubscription? _languageListener; + Future _setLanguageListener() async { + await pangeaController.userController.initialize(); + _languageListener?.cancel(); + _languageListener = pangeaController.userController.languageStream.stream + .listen((_) => _setAppLanguage()); + } + + void _setAppLanguage() { + try { + Provider.of(context, listen: false).setLocale( + pangeaController.userController.profile.userSettings.sourceLanguage, + ); + } catch (e, s) { + Logs().e('Error setting app language', e); + ErrorHandler.logError( + e: e, + s: s, + data: {}, + ); + } + } + // Pangea# + Future initConfig() async { try { final configJsonString = @@ -320,6 +352,7 @@ class MatrixState extends State with WidgetsBindingObserver { MatrixState.pangeaController.handleLoginStateChange( state, c.userID, + context, ); // Pangea# final loggedInWithMultipleClients = widget.clients.length > 1; @@ -500,6 +533,9 @@ class MatrixState extends State with WidgetsBindingObserver { onBlurSub?.cancel(); linuxNotifications?.close(); + // #Pangea + _languageListener?.cancel(); + // Pangea# super.dispose(); }