diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart index 30ce61385..e92a1fefd 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart @@ -45,9 +45,12 @@ extension ActivityMenuLogic on ChatController { return false; } + final l1 = + MatrixState.pangeaController.userController.userL1?.langCodeShort; final l2 = MatrixState.pangeaController.userController.userL2?.langCodeShort; + final activityLang = room.activityPlan?.req.targetLanguage.split('-').first; - return activityLang != null && l2 != activityLang; + return activityLang != null && activityLang != l1 && l2 != activityLang; } } diff --git a/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart b/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart index 5293b9981..3029294d7 100644 --- a/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart +++ b/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart @@ -14,6 +14,8 @@ import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dar import 'package:fluffychat/pangea/learning_settings/p_language_dropdown.dart'; import 'package:fluffychat/pangea/learning_settings/voice_dropdown.dart'; import 'package:fluffychat/pangea/user/user_model.dart' as user; +import 'package:fluffychat/utils/localized_exception_extension.dart'; +import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart'; import 'package:fluffychat/widgets/matrix.dart'; class BotChatSettingsDialog extends StatefulWidget { @@ -50,7 +52,10 @@ class BotChatSettingsDialogState extends State { user.Profile get _userProfile => MatrixState.pangeaController.userController.profile; - Future _update(user.Profile Function(user.Profile) update) async { + Future _update( + user.Profile Function(user.Profile) update, + VoidCallback reset, + ) async { try { await MatrixState.pangeaController.userController .updateProfile(update, waitForDataInSync: true) @@ -59,50 +64,117 @@ class BotChatSettingsDialogState extends State { context, ).client.updateBotOptions(_userProfile.userSettings); } catch (e, s) { + reset(); ErrorHandler.logError( e: e, s: s, data: {'roomId': widget.room.id, 'model': _userProfile.toJson()}, ); + await _showErrorDialog(e); } } + Future _showErrorDialog(Object error) async { + await showDialog( + context: context, + builder: (context) { + return AlertDialog.adaptive( + title: Icon( + Icons.error_outline_outlined, + color: Theme.of(context).colorScheme.error, + size: 48, + ), + content: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 256), + child: Row( + crossAxisAlignment: .center, + children: [ + Expanded( + child: Text( + error.toLocalizedString(context), + maxLines: 4, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ), + actions: [ + AdaptiveDialogAction( + onPressed: () => Navigator.of(context).pop(), + child: Text(L10n.of(context).close), + ), + ], + ); + }, + ); + } + Future _setLanguage(LanguageModel? lang) async { if (lang == null || lang.langCode == _userProfile.userSettings.targetLanguage) { return; } + final prevLang = MatrixState.pangeaController.userController.userL2; + final prevVoice = _userProfile.userSettings.voice; + setState(() { _selectedLang = lang; _selectedVoice = null; }); - await _update((model) { - model.userSettings.targetLanguage = lang.langCode; - model.userSettings.voice = null; - return model; - }); + await _update( + (model) { + model.userSettings.targetLanguage = lang.langCode; + model.userSettings.voice = null; + return model; + }, + () { + if (mounted) { + setState(() { + _selectedLang = prevLang; + _selectedVoice = prevVoice; + }); + } + }, + ); } Future _setLevel(LanguageLevelTypeEnum? level) async { if (level == null || level == _userProfile.userSettings.cefrLevel) return; + final prevLevel = _userProfile.userSettings.cefrLevel; setState(() => _selectedLevel = level); - await _update((model) { - model.userSettings.cefrLevel = level; - return model; - }); + await _update( + (model) { + model.userSettings.cefrLevel = level; + return model; + }, + () { + if (mounted) { + setState(() => _selectedLevel = prevLevel); + } + }, + ); } Future _setVoice(String? voice) async { if (voice == _userProfile.userSettings.voice) return; + final prevVoice = _userProfile.userSettings.voice; setState(() => _selectedVoice = voice); - await _update((model) { - model.userSettings.voice = voice; - return model; - }); + await _update( + (model) { + model.userSettings.voice = voice; + return model; + }, + () { + if (mounted) { + setState(() => _selectedVoice = prevVoice); + } + }, + ); } @override diff --git a/lib/pangea/learning_settings/language_mismatch_popup.dart b/lib/pangea/learning_settings/language_mismatch_popup.dart index c0e963fe9..f0583f1f5 100644 --- a/lib/pangea/learning_settings/language_mismatch_popup.dart +++ b/lib/pangea/learning_settings/language_mismatch_popup.dart @@ -6,6 +6,8 @@ import 'package:fluffychat/pangea/common/widgets/card_header.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/matrix.dart'; +class IdenticalLanguageException implements Exception {} + class LanguageMismatchPopup extends StatelessWidget { final String message; final String overlayId; diff --git a/lib/pangea/login/pages/language_selection_page.dart b/lib/pangea/login/pages/language_selection_page.dart index 3f22fb96a..ae1859795 100644 --- a/lib/pangea/login/pages/language_selection_page.dart +++ b/lib/pangea/login/pages/language_selection_page.dart @@ -9,12 +9,11 @@ import 'package:fluffychat/pangea/common/widgets/shrinkable_text.dart'; import 'package:fluffychat/pangea/languages/language_model.dart'; import 'package:fluffychat/pangea/languages/language_service.dart'; import 'package:fluffychat/pangea/languages/p_language_store.dart'; +import 'package:fluffychat/pangea/learning_settings/language_mismatch_popup.dart'; import 'package:fluffychat/pangea/learning_settings/p_language_dropdown.dart'; import 'package:fluffychat/pangea/login/utils/lang_code_repo.dart'; import 'package:fluffychat/widgets/matrix.dart'; -class IdenticalLanguageException implements Exception {} - class LanguageSelectionPage extends StatefulWidget { const LanguageSelectionPage({super.key}); diff --git a/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart b/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart index 6db9b759e..c25e582b0 100644 --- a/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart +++ b/lib/pangea/toolbar/reading_assistance/select_mode_buttons.dart @@ -237,6 +237,8 @@ class SelectModeButtonsState extends State { Future modeDisabled() async { final target = controller.messageEvent.originalSent?.langCode; + final l1 = + MatrixState.pangeaController.userController.userL1?.langCodeShort; final messenger = ScaffoldMessenger.of(context); messenger.hideCurrentSnackBar(); messenger.showSnackBar( @@ -250,7 +252,7 @@ class SelectModeButtonsState extends State { textAlign: TextAlign.center, ), ), - if (target != null) + if (target != null && target != l1) TextButton( style: TextButton.styleFrom( foregroundColor: Theme.of( diff --git a/lib/pangea/user/user_controller.dart b/lib/pangea/user/user_controller.dart index 89483ff5c..3b58d1fb9 100644 --- a/lib/pangea/user/user_controller.dart +++ b/lib/pangea/user/user_controller.dart @@ -13,6 +13,7 @@ import 'package:fluffychat/pangea/languages/language_constants.dart'; import 'package:fluffychat/pangea/languages/language_model.dart'; import 'package:fluffychat/pangea/languages/language_service.dart'; import 'package:fluffychat/pangea/languages/p_language_store.dart'; +import 'package:fluffychat/pangea/learning_settings/language_mismatch_popup.dart'; import 'package:fluffychat/pangea/learning_settings/tool_settings_enum.dart'; import 'package:fluffychat/pangea/user/analytics_profile_model.dart'; import 'package:fluffychat/pangea/user/public_profile_model.dart'; @@ -124,7 +125,21 @@ class UserController { await initialize(); final prevHash = profile.hashCode; - final Profile updatedProfile = update(profile); + final Profile updatedProfile = update(profile.copy()); + + final sourceCodeShort = updatedProfile.userSettings.sourceLanguage + ?.split("-") + .first; + final targetCodeShort = updatedProfile.userSettings.targetLanguage + ?.split("-") + .first; + + if (sourceCodeShort != null && + targetCodeShort != null && + sourceCodeShort == targetCodeShort) { + throw IdenticalLanguageException(); + } + if (updatedProfile.hashCode == prevHash) { // no changes were made, so don't save return; diff --git a/lib/utils/localized_exception_extension.dart b/lib/utils/localized_exception_extension.dart index 1cdfee231..3b1498027 100644 --- a/lib/utils/localized_exception_extension.dart +++ b/lib/utils/localized_exception_extension.dart @@ -11,6 +11,7 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pages/chat/recording_view_model.dart'; import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_repo.dart'; import 'package:fluffychat/pangea/common/network/requests.dart'; +import 'package:fluffychat/pangea/learning_settings/language_mismatch_popup.dart'; import 'package:fluffychat/utils/other_party_can_receive.dart'; import 'uia_request_manager.dart'; @@ -44,6 +45,10 @@ extension LocalizedExceptionExtension on Object { if (this is EmptyAudioException) { return L10n.of(context).emptyAudioError; } + + if (this is IdenticalLanguageException) { + return L10n.of(context).noIdenticalLanguages; + } // Pangea# if (this is FileTooBigMatrixException) { final exception = this as FileTooBigMatrixException;