From 0e92428327da1909e7ca815b7ce4731f72af0ec8 Mon Sep 17 00:00:00 2001 From: Ava Shilling <165050625+avashilling@users.noreply.github.com> Date: Tue, 20 Jan 2026 12:22:33 -0500 Subject: [PATCH 01/18] chore: only show shimmer on most recent message --- lib/pages/chat/events/message.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index d3ac2306b..6344caa36 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -606,9 +606,10 @@ class Message extends StatelessWidget { // #Pangea child: ShimmerBackground( enabled: controller - .showMessageShimmer( - event, - ), + .showMessageShimmer( + event, + ) && + isButton, // Pangea# child: Container( decoration: From bf5b75a2560d4dd5c0fd5142604a16f81f9fba70 Mon Sep 17 00:00:00 2001 From: Ava Shilling <165050625+avashilling@users.noreply.github.com> Date: Tue, 20 Jan 2026 14:54:58 -0500 Subject: [PATCH 02/18] chore: check for button in showMessageShimmer --- lib/pages/chat/chat.dart | 1 + lib/pages/chat/events/message.dart | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 18a21f926..045344fcf 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -2010,6 +2010,7 @@ class ChatController extends State bool showMessageShimmer(Event event) { if (event.type != EventTypes.Message) return false; + if (!(event.eventId == buttonEventID)) return false; if (event.messageType == MessageTypes.Text) { return !InstructionsEnum.clickTextMessages.isToggledOff; } diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 6344caa36..d3ac2306b 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -606,10 +606,9 @@ class Message extends StatelessWidget { // #Pangea child: ShimmerBackground( enabled: controller - .showMessageShimmer( - event, - ) && - isButton, + .showMessageShimmer( + event, + ), // Pangea# child: Container( decoration: From da8b99b78b5475c76e6490e6c2bb4c0a472e6e54 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 15:02:03 -0500 Subject: [PATCH 03/18] fix: show error message when not enough data for practice --- .../analytics_practice_session_repo.dart | 6 ++++++ lib/pangea/analytics_practice/analytics_practice_view.dart | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/pangea/analytics_practice/analytics_practice_session_repo.dart b/lib/pangea/analytics_practice/analytics_practice_session_repo.dart index 378dc4486..d50407c1b 100644 --- a/lib/pangea/analytics_practice/analytics_practice_session_repo.dart +++ b/lib/pangea/analytics_practice/analytics_practice_session_repo.dart @@ -15,6 +15,8 @@ import 'package:fluffychat/pangea/practice_activities/message_activity_request.d import 'package:fluffychat/pangea/practice_activities/practice_target.dart'; import 'package:fluffychat/widgets/matrix.dart'; +class InsufficientDataException implements Exception {} + class AnalyticsPracticeSessionRepo { static Future get( ConstructTypeEnum type, @@ -67,6 +69,10 @@ class AnalyticsPracticeSessionRepo { } } + if (targets.isEmpty) { + throw InsufficientDataException(); + } + final session = AnalyticsPracticeSessionModel( userL1: MatrixState.pangeaController.userController.userL1!.langCode, userL2: MatrixState.pangeaController.userController.userL2!.langCode, diff --git a/lib/pangea/analytics_practice/analytics_practice_view.dart b/lib/pangea/analytics_practice/analytics_practice_view.dart index 6cbef9eae..e5e57404b 100644 --- a/lib/pangea/analytics_practice/analytics_practice_view.dart +++ b/lib/pangea/analytics_practice/analytics_practice_view.dart @@ -5,6 +5,7 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; import 'package:fluffychat/pangea/analytics_practice/analytics_practice_page.dart'; import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_model.dart'; +import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_repo.dart'; import 'package:fluffychat/pangea/analytics_practice/choice_cards/audio_choice_card.dart'; import 'package:fluffychat/pangea/analytics_practice/choice_cards/game_choice_card.dart'; import 'package:fluffychat/pangea/analytics_practice/choice_cards/grammar_choice_card.dart'; @@ -84,7 +85,11 @@ class AnalyticsPracticeView extends StatelessWidget { builder: (context, state, __) { return switch (state) { AsyncError(:final error) => - ErrorIndicator(message: error.toString()), + ErrorIndicator( + message: error is InsufficientDataException + ? L10n.of(context).notEnoughToPractice + : error.toString(), + ), AsyncLoaded(:final value) => value.isComplete ? CompletedActivitySessionView(state.value, controller) From 5008ce70557d954c8fecf008b1d6d86ee87267db Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 15:18:31 -0500 Subject: [PATCH 04/18] fix: clear selected token in activity vocab display on word card dismissed --- .../activity_vocab_widget.dart | 81 ++++++++++++++----- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart index a7fe9e7f6..0a05e5c47 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart @@ -106,27 +106,17 @@ class _VocabChipsState extends State<_VocabChips> with TokenRenderingMixin { OverlayUtil.showPositionedCard( overlayKey: target, context: context, - cardToShow: StatefulBuilder( - builder: (context, setState) => WordZoomWidget( - token: PangeaTokenText( - content: v.lemma, - length: v.lemma.characters.length, - offset: 0, - ), - construct: ConstructIdentifier( - lemma: v.lemma, - type: ConstructTypeEnum.vocab, - category: v.pos, - ), - langCode: widget.langCode, - onClose: () { - MatrixState.pAnyState.closeOverlay(target); - setState(() => _selectedVocab = null); - }, - onDismissNewWordOverlay: () { - if (mounted) setState(() {}); - }, - ), + cardToShow: _WordCardWrapper( + v: v, + langCode: widget.langCode, + target: target, + onClose: () { + if (mounted) { + WidgetsBinding.instance.addPostFrameCallback( + (_) => setState(() => _selectedVocab = null), + ); + } + }, ), transformTargetId: target, closePrevOverlay: false, @@ -204,3 +194,52 @@ class _VocabChipsState extends State<_VocabChips> with TokenRenderingMixin { ); } } + +class _WordCardWrapper extends StatefulWidget { + final Vocab v; + final String langCode; + final String target; + final VoidCallback onClose; + + const _WordCardWrapper({ + required this.v, + required this.langCode, + required this.target, + required this.onClose, + }); + + @override + State<_WordCardWrapper> createState() => _WordCardWrapperState(); +} + +class _WordCardWrapperState extends State<_WordCardWrapper> { + @override + void dispose() { + widget.onClose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return WordZoomWidget( + token: PangeaTokenText( + content: widget.v.lemma, + length: widget.v.lemma.characters.length, + offset: 0, + ), + construct: ConstructIdentifier( + lemma: widget.v.lemma, + type: ConstructTypeEnum.vocab, + category: widget.v.pos, + ), + langCode: widget.langCode, + onClose: () { + MatrixState.pAnyState.closeOverlay(widget.target); + widget.onClose(); + }, + onDismissNewWordOverlay: () { + if (mounted) setState(() {}); + }, + ); + } +} From a8c4b1d7c2583d0d5d63a4b4fb5c2bdcb68357c3 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 15:24:38 -0500 Subject: [PATCH 05/18] chore: throw expection while loading practice session is user is unsubscribed --- lib/pangea/analytics_practice/analytics_practice_page.dart | 2 ++ .../analytics_practice/analytics_practice_session_repo.dart | 6 ++++++ lib/pangea/analytics_practice/analytics_practice_view.dart | 6 ++---- lib/utils/localized_exception_extension.dart | 5 +++++ 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/pangea/analytics_practice/analytics_practice_page.dart b/lib/pangea/analytics_practice/analytics_practice_page.dart index 97ca1d1a7..9f3c0573e 100644 --- a/lib/pangea/analytics_practice/analytics_practice_page.dart +++ b/lib/pangea/analytics_practice/analytics_practice_page.dart @@ -245,6 +245,8 @@ class AnalyticsPracticeState extends State Future _startSession() async { await _waitForAnalytics(); await _sessionLoader.load(); + if (_sessionLoader.isError) return; + progressNotifier.value = _sessionLoader.value!.progress; await _continueSession(); } diff --git a/lib/pangea/analytics_practice/analytics_practice_session_repo.dart b/lib/pangea/analytics_practice/analytics_practice_session_repo.dart index d50407c1b..37fde47fd 100644 --- a/lib/pangea/analytics_practice/analytics_practice_session_repo.dart +++ b/lib/pangea/analytics_practice/analytics_practice_session_repo.dart @@ -4,6 +4,7 @@ import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart'; import 'package:fluffychat/pangea/analytics_practice/analytics_practice_constants.dart'; import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_model.dart'; +import 'package:fluffychat/pangea/common/network/requests.dart'; import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; @@ -21,6 +22,11 @@ class AnalyticsPracticeSessionRepo { static Future get( ConstructTypeEnum type, ) async { + if (MatrixState.pangeaController.subscriptionController.isSubscribed == + false) { + throw UnsubscribedException(); + } + final r = Random(); final activityTypes = ActivityTypeEnum.analyticsPracticeTypes(type); diff --git a/lib/pangea/analytics_practice/analytics_practice_view.dart b/lib/pangea/analytics_practice/analytics_practice_view.dart index e5e57404b..1d7c04031 100644 --- a/lib/pangea/analytics_practice/analytics_practice_view.dart +++ b/lib/pangea/analytics_practice/analytics_practice_view.dart @@ -5,7 +5,6 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; import 'package:fluffychat/pangea/analytics_practice/analytics_practice_page.dart'; import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_model.dart'; -import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_repo.dart'; import 'package:fluffychat/pangea/analytics_practice/choice_cards/audio_choice_card.dart'; import 'package:fluffychat/pangea/analytics_practice/choice_cards/game_choice_card.dart'; import 'package:fluffychat/pangea/analytics_practice/choice_cards/grammar_choice_card.dart'; @@ -20,6 +19,7 @@ import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart' import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_widget.dart'; import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart'; import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart'; +import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -86,9 +86,7 @@ class AnalyticsPracticeView extends StatelessWidget { return switch (state) { AsyncError(:final error) => ErrorIndicator( - message: error is InsufficientDataException - ? L10n.of(context).notEnoughToPractice - : error.toString(), + message: error.toLocalizedString(context), ), AsyncLoaded(:final value) => value.isComplete diff --git a/lib/utils/localized_exception_extension.dart b/lib/utils/localized_exception_extension.dart index d50dbbbfa..17df8cf58 100644 --- a/lib/utils/localized_exception_extension.dart +++ b/lib/utils/localized_exception_extension.dart @@ -8,6 +8,7 @@ import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_repo.dart'; import 'package:fluffychat/pangea/common/network/requests.dart'; import 'package:fluffychat/utils/other_party_can_receive.dart'; import 'uia_request_manager.dart'; @@ -34,6 +35,10 @@ extension LocalizedExceptionExtension on Object { if (this is UnsubscribedException) { return L10n.of(context).unsubscribedResponseError; } + + if (this is InsufficientDataException) { + return L10n.of(context).notEnoughToPractice; + } // Pangea# if (this is FileTooBigMatrixException) { final exception = this as FileTooBigMatrixException; From e31f4d5b418382231d51b6232337c4caafcbc8b4 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 16:03:30 -0500 Subject: [PATCH 06/18] fix: account for blocked and capped constructs in analytics download model --- .../space_analytics_summary_model.dart | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/pangea/analytics_downloads/space_analytics_summary_model.dart b/lib/pangea/analytics_downloads/space_analytics_summary_model.dart index ddccf10e9..a176fff8d 100644 --- a/lib/pangea/analytics_downloads/space_analytics_summary_model.dart +++ b/lib/pangea/analytics_downloads/space_analytics_summary_model.dart @@ -97,7 +97,6 @@ class SpaceAnalyticsSummaryModel { Set blockedConstructs, int numCompletedActivities, ) { - int totalXP = 0; int numWordsTyped = 0; int numChoicesCorrect = 0; int numChoicesIncorrect = 0; @@ -114,7 +113,9 @@ class SpaceAnalyticsSummaryModel { mergeTable.addConstructsByUses(e.content.uses, blockedConstructs); for (final use in e.content.uses) { - totalXP += use.xp; + final id = use.identifier; + if (blockedConstructs.contains(id)) continue; + allUses.add(use); if (use.useType.summaryEnumType == @@ -132,8 +133,7 @@ class SpaceAnalyticsSummaryModel { sentEventIds.add(use.metadata.eventId!); } - final id = use.identifier; - final existing = id.type == ConstructTypeEnum.vocab + final existing = use.identifier.type == ConstructTypeEnum.vocab ? aggregatedVocab[id] : aggregatedMorph[id]; @@ -189,6 +189,10 @@ class SpaceAnalyticsSummaryModel { } } + final totalXP = cleanedVocab.values + .fold(0, (sum, entry) => sum + entry.points) + + cleanedMorph.values.fold(0, (sum, entry) => sum + entry.points); + final level = DerivedAnalyticsDataModel.calculateLevelWithXp(totalXP); final uniqueVocabCount = cleanedVocab.length; final uniqueMorphCount = cleanedMorph.length; From 2701f747596949f6abc6675a018f089044ddcfd2 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 16:15:32 -0500 Subject: [PATCH 07/18] chore: save voice in TTS events and re-request if requested voice doesn't match saved voice --- lib/pangea/common/constants/model_keys.dart | 1 + .../event_wrappers/pangea_message_event.dart | 14 ++++++++++---- .../text_to_speech_response_model.dart | 11 ++++++++++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/pangea/common/constants/model_keys.dart b/lib/pangea/common/constants/model_keys.dart index 9296826b9..d248e680e 100644 --- a/lib/pangea/common/constants/model_keys.dart +++ b/lib/pangea/common/constants/model_keys.dart @@ -109,6 +109,7 @@ class ModelKey { static const String transcription = "transcription"; static const String botTranscription = 'bot_transcription'; + static const String voice = "voice"; // bot options static const String languageLevel = "difficulty"; diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index 22cf8ca1d..66d2395d1 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -303,7 +303,11 @@ class PangeaMessageEvent { return null; } - Event? getTextToSpeechLocal(String langCode, String text) { + Event? getTextToSpeechLocal( + String langCode, + String text, + String? voice, + ) { for (final audio in allAudio) { final dataMap = audio.content.tryGetMap(ModelKey.transcription); if (dataMap == null || !dataMap.containsKey(ModelKey.tokens)) continue; @@ -313,7 +317,9 @@ class PangeaMessageEvent { dataMap as dynamic, ); - if (audioData.langCode == langCode && audioData.text == text) { + if (audioData.langCode == langCode && + audioData.text == text && + audioData.voice == voice) { return audio; } } catch (e, s) { @@ -368,7 +374,7 @@ class PangeaMessageEvent { String langCode, String? voice, ) async { - final local = getTextToSpeechLocal(langCode, messageDisplayText); + final local = getTextToSpeechLocal(langCode, messageDisplayText, voice); if (local != null) { final file = await local.getPangeaAudioFile(); if (file != null) return file; @@ -424,7 +430,7 @@ class PangeaMessageEvent { 'waveform': response.waveform, }, ModelKey.transcription: response - .toPangeaAudioEventData(rep?.text ?? body, langCode) + .toPangeaAudioEventData(rep?.text ?? body, langCode, voice) .toJson(), }, ).then((eventId) async { diff --git a/lib/pangea/text_to_speech/text_to_speech_response_model.dart b/lib/pangea/text_to_speech/text_to_speech_response_model.dart index 24cc71e1a..645005a3d 100644 --- a/lib/pangea/text_to_speech/text_to_speech_response_model.dart +++ b/lib/pangea/text_to_speech/text_to_speech_response_model.dart @@ -41,11 +41,16 @@ class TextToSpeechResponseModel { "tts_tokens": List.from(ttsTokens.map((x) => x.toJson())), }; - PangeaAudioEventData toPangeaAudioEventData(String text, String langCode) { + PangeaAudioEventData toPangeaAudioEventData( + String text, + String langCode, + String? voice, + ) { return PangeaAudioEventData( text: text, langCode: langCode, tokens: ttsTokens, + voice: voice, ); } } @@ -91,11 +96,13 @@ class PangeaAudioEventData { final String text; final String langCode; final List tokens; + final String? voice; PangeaAudioEventData({ required this.text, required this.langCode, required this.tokens, + this.voice, }); factory PangeaAudioEventData.fromJson(dynamic json) => PangeaAudioEventData( @@ -106,6 +113,7 @@ class PangeaAudioEventData { .map((x) => TTSToken.fromJson(x)) .toList(), ), + voice: json[ModelKey.voice] as String?, ); Map toJson() => { @@ -113,5 +121,6 @@ class PangeaAudioEventData { ModelKey.langCode: langCode, ModelKey.tokens: List>.from(tokens.map((x) => x.toJson())), + if (voice != null) ModelKey.voice: voice, }; } From 0d346269961e08e90dfcb61ac4e8615b6d18edc9 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 16:28:17 -0500 Subject: [PATCH 08/18] fix: filter RoomMemberChangeType.other events from timeline --- lib/pangea/course_chats/course_chats_page.dart | 5 +++-- .../matrix_sdk_extensions/filtered_timeline_extension.dart | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pangea/course_chats/course_chats_page.dart b/lib/pangea/course_chats/course_chats_page.dart index 6945aadfe..1b962497b 100644 --- a/lib/pangea/course_chats/course_chats_page.dart +++ b/lib/pangea/course_chats/course_chats_page.dart @@ -57,7 +57,9 @@ class CourseChatsController extends State @override void initState() { - loadHierarchy(reload: true); + loadHierarchy(reload: true).then( + (_) => _joinDefaultChats(), + ); // Listen for changes to the activeSpace's hierarchy, // and reload the hierarchy when they come through @@ -212,7 +214,6 @@ class CourseChatsController extends State try { await _loadHierarchy(activeSpace: room, reload: reload); - if (mounted) await _joinDefaultChats(); if (mounted) { final futures = [ loadRoomSummaries( diff --git a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart index 65bd3119c..f15e1f119 100644 --- a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart +++ b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart @@ -64,7 +64,8 @@ extension IsStateExtension on Event { bool get isVisibleInPangeaGui { if (!room.showActivityChatUI) { return type != EventTypes.RoomMember || - roomMemberChangeType != RoomMemberChangeType.avatar; + (roomMemberChangeType != RoomMemberChangeType.avatar && + roomMemberChangeType != RoomMemberChangeType.other); } return type != EventTypes.RoomMember; From 819527cbd86a44e539b389b9b6ccf74b21fb228a Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 20 Jan 2026 16:52:37 -0500 Subject: [PATCH 09/18] chore: store font size settings per-user --- lib/pages/settings_style/settings_style.dart | 16 +++++--- .../common/controllers/pangea_controller.dart | 14 ++++--- lib/pangea/user/style_settings_repo.dart | 40 +++++++++++++++++++ lib/widgets/matrix.dart | 14 +++++-- 4 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 lib/pangea/user/style_settings_repo.dart diff --git a/lib/pages/settings_style/settings_style.dart b/lib/pages/settings_style/settings_style.dart index 49b0b003c..5a0092a9a 100644 --- a/lib/pages/settings_style/settings_style.dart +++ b/lib/pages/settings_style/settings_style.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/config/setting_keys.dart'; +import 'package:fluffychat/pangea/user/style_settings_repo.dart'; import 'package:fluffychat/utils/account_config.dart'; import 'package:fluffychat/utils/file_selector.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; @@ -157,10 +157,16 @@ class SettingsStyleController extends State { void changeFontSizeFactor(double d) { setState(() => AppConfig.fontSizeFactor = d); - Matrix.of(context).store.setString( - SettingKeys.fontSizeFactor, - AppConfig.fontSizeFactor.toString(), - ); + // #Pangea + // Matrix.of(context).store.setString( + // SettingKeys.fontSizeFactor, + // AppConfig.fontSizeFactor.toString(), + // ); + StyleSettingsRepo.setFontSizeFactor( + Matrix.of(context).client.userID!, + AppConfig.fontSizeFactor, + ); + // Pangea# } @override diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index b1339a3e4..5257753d3 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -18,6 +18,7 @@ import 'package:fluffychat/pangea/languages/p_language_store.dart'; import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart'; import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; import 'package:fluffychat/pangea/user/pangea_push_rules_extension.dart'; +import 'package:fluffychat/pangea/user/style_settings_repo.dart'; import 'package:fluffychat/pangea/user/user_controller.dart'; import 'package:fluffychat/widgets/matrix.dart'; import '../utils/firebase_analytics.dart'; @@ -55,7 +56,7 @@ class PangeaController { TtsController.setAvailableLanguages(); } - void _onLogin(BuildContext context) { + void _onLogin(BuildContext context, String? userID) { initControllers(); _registerSubscriptions(); @@ -64,6 +65,10 @@ class PangeaController { Provider.of(context, listen: false).setLocale(l1); }); subscriptionController.reinitialize(); + + StyleSettingsRepo.fontSizeFactor(userID!).then((factor) { + AppConfig.fontSizeFactor = factor; + }); } void _onLogout(BuildContext context) { @@ -91,7 +96,7 @@ class PangeaController { _onLogout(context); break; case LoginState.loggedIn: - _onLogin(context); + _onLogin(context, userID); break; } @@ -122,9 +127,7 @@ class PangeaController { } Future _clearCache({List exclude = const []}) async { - final List> futures = [ - matrixState.store.setString(SettingKeys.fontSizeFactor, ''), - ]; + final List> futures = []; for (final key in _storageKeys) { if (exclude.contains(key)) continue; futures.add(GetStorage(key).erase()); @@ -142,7 +145,6 @@ class PangeaController { ); } - AppConfig.fontSizeFactor = 1.0; await Future.wait(futures); } diff --git a/lib/pangea/user/style_settings_repo.dart b/lib/pangea/user/style_settings_repo.dart new file mode 100644 index 000000000..16fae1236 --- /dev/null +++ b/lib/pangea/user/style_settings_repo.dart @@ -0,0 +1,40 @@ +import 'package:get_storage/get_storage.dart'; + +class _StyleSettings { + final double fontSizeFactor; + + const _StyleSettings({ + this.fontSizeFactor = 1.0, + }); + + Map toJson() { + return { + 'fontSizeFactor': fontSizeFactor, + }; + } + + factory _StyleSettings.fromJson(Map json) { + return _StyleSettings( + fontSizeFactor: (json['fontSizeFactor'] as num?)?.toDouble() ?? 1.0, + ); + } +} + +class StyleSettingsRepo { + static final GetStorage _storage = GetStorage("style_settings"); + + static Future fontSizeFactor(String userId) async { + await GetStorage.init("style_settings"); + final json = + _storage.read>('${userId}_style_settings'); + final settings = + json != null ? _StyleSettings.fromJson(json) : const _StyleSettings(); + return settings.fontSizeFactor; + } + + static Future setFontSizeFactor(String userId, double factor) async { + await GetStorage.init("style_settings"); + final settings = _StyleSettings(fontSizeFactor: factor); + await _storage.write('${userId}_style_settings', settings.toJson()); + } +} diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index ef3a4b537..78544c300 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -26,6 +26,7 @@ import 'package:fluffychat/pangea/common/utils/any_state_holder.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/join_codes/space_code_controller.dart'; import 'package:fluffychat/pangea/languages/locale_provider.dart'; +import 'package:fluffychat/pangea/user/style_settings_repo.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'; @@ -553,9 +554,16 @@ class MatrixState extends State with WidgetsBindingObserver { } void initSettings() { - AppConfig.fontSizeFactor = - double.tryParse(store.getString(SettingKeys.fontSizeFactor) ?? '') ?? - AppConfig.fontSizeFactor; + // #Pangea + // AppConfig.fontSizeFactor = + // double.tryParse(store.getString(SettingKeys.fontSizeFactor) ?? '') ?? + // AppConfig.fontSizeFactor; + if (client.isLogged()) { + StyleSettingsRepo.fontSizeFactor(client.userID!).then((factor) { + AppConfig.fontSizeFactor = factor; + }); + } + // Pangea# AppConfig.renderHtml = store.getBool(SettingKeys.renderHtml) ?? AppConfig.renderHtml; From 8a2f4747c99068fdaef552938937ccac8af6fc42 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 09:16:55 -0500 Subject: [PATCH 10/18] fix: oops, don't return null from representationByLanguage (#5301) --- .../event_wrappers/pangea_message_event.dart | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index 66d2395d1..de462ed95 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -294,14 +294,12 @@ class PangeaMessageEvent { RepresentationEvent? representationByLanguage( String langCode, { bool Function(RepresentationEvent)? filter, - }) { - representations.firstWhereOrNull( - (element) => - element.langCode.split("-")[0] == langCode.split("-")[0] && - (filter?.call(element) ?? true), - ); - return null; - } + }) => + representations.firstWhereOrNull( + (element) => + element.langCode.split("-")[0] == langCode.split("-")[0] && + (filter?.call(element) ?? true), + ); Event? getTextToSpeechLocal( String langCode, From 529f12e0288c10a10e99863bcc6ee02bc919b6f1 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 10:44:21 -0500 Subject: [PATCH 11/18] 5259 bot settings language settings (#5305) * feat: add voice to user model * update bot settings on language / learning settings update * use room summary to determine member count * translations --- lib/l10n/intl_ar.arb | 7 +- lib/l10n/intl_be.arb | 7 +- lib/l10n/intl_bn.arb | 7 +- lib/l10n/intl_bo.arb | 7 +- lib/l10n/intl_ca.arb | 7 +- lib/l10n/intl_cs.arb | 7 +- lib/l10n/intl_da.arb | 7 +- lib/l10n/intl_de.arb | 7 +- lib/l10n/intl_el.arb | 7 +- lib/l10n/intl_en.arb | 3 +- lib/l10n/intl_eo.arb | 7 +- lib/l10n/intl_es.arb | 7 +- lib/l10n/intl_et.arb | 7 +- lib/l10n/intl_eu.arb | 7 +- lib/l10n/intl_fa.arb | 7 +- lib/l10n/intl_fi.arb | 7 +- lib/l10n/intl_fil.arb | 7 +- lib/l10n/intl_fr.arb | 7 +- lib/l10n/intl_ga.arb | 7 +- lib/l10n/intl_gl.arb | 7 +- lib/l10n/intl_he.arb | 7 +- lib/l10n/intl_hi.arb | 7 +- lib/l10n/intl_hr.arb | 7 +- lib/l10n/intl_hu.arb | 7 +- lib/l10n/intl_ia.arb | 7 +- lib/l10n/intl_id.arb | 7 +- lib/l10n/intl_ie.arb | 7 +- lib/l10n/intl_it.arb | 7 +- lib/l10n/intl_ja.arb | 7 +- lib/l10n/intl_ka.arb | 7 +- lib/l10n/intl_ko.arb | 7 +- lib/l10n/intl_lt.arb | 7 +- lib/l10n/intl_lv.arb | 7 +- lib/l10n/intl_nb.arb | 7 +- lib/l10n/intl_nl.arb | 7 +- lib/l10n/intl_pl.arb | 7 +- lib/l10n/intl_pt.arb | 7 +- lib/l10n/intl_pt_BR.arb | 7 +- lib/l10n/intl_pt_PT.arb | 7 +- lib/l10n/intl_ro.arb | 7 +- lib/l10n/intl_ru.arb | 7 +- lib/l10n/intl_sk.arb | 7 +- lib/l10n/intl_sl.arb | 7 +- lib/l10n/intl_sr.arb | 7 +- lib/l10n/intl_sv.arb | 7 +- lib/l10n/intl_ta.arb | 7 +- lib/l10n/intl_te.arb | 7 +- lib/l10n/intl_th.arb | 7 +- lib/l10n/intl_tr.arb | 7 +- lib/l10n/intl_uk.arb | 7 +- lib/l10n/intl_vi.arb | 7 +- lib/l10n/intl_yue.arb | 7 +- lib/l10n/intl_zh.arb | 7 +- lib/l10n/intl_zh_Hant.arb | 7 +- lib/pages/chat/events/message.dart | 14 - .../activity_participant_indicator.dart | 10 - .../bot/widgets/bot_chat_settings_dialog.dart | 194 ------------- .../widgets/bot_settings_language_icon.dart | 57 ---- .../models/bot_options_model.dart | 117 ++++---- .../utils/bot_client_extension.dart | 60 ++-- .../common/controllers/pangea_controller.dart | 7 +- .../p_settings_switch_list_tile.dart | 1 - .../learning_settings/settings_learning.dart | 35 ++- .../settings_learning_view.dart | 273 ++++++++---------- .../learning_settings/voice_dropdown.dart | 53 ++++ .../message_practice/message_audio_card.dart | 4 +- .../select_mode_controller.dart | 3 +- lib/pangea/user/user_controller.dart | 2 + lib/pangea/user/user_model.dart | 9 +- lib/widgets/avatar.dart | 10 +- .../member_actions_popup_menu_button.dart | 20 +- 71 files changed, 625 insertions(+), 618 deletions(-) delete mode 100644 lib/pangea/bot/widgets/bot_chat_settings_dialog.dart delete mode 100644 lib/pangea/bot/widgets/bot_settings_language_icon.dart create mode 100644 lib/pangea/learning_settings/voice_dropdown.dart diff --git a/lib/l10n/intl_ar.arb b/lib/l10n/intl_ar.arb index 70fa0fc97..3642fb278 100644 --- a/lib/l10n/intl_ar.arb +++ b/lib/l10n/intl_ar.arb @@ -1,6 +1,6 @@ { "@@locale": "ar", - "@@last_modified": "2026-01-20 12:31:24.671375", + "@@last_modified": "2026-01-21 10:42:52.513008", "about": "حول", "@about": { "type": "String", @@ -11107,5 +11107,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "صوت بوت بانجيا", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_be.arb b/lib/l10n/intl_be.arb index d1ff0babf..f7593b805 100644 --- a/lib/l10n/intl_be.arb +++ b/lib/l10n/intl_be.arb @@ -1911,7 +1911,7 @@ "playWithAI": "Пакуль гуляйце з ШІ", "courseStartDesc": "Pangea Bot гатовы да працы ў любы час!\n\n...але навучанне лепш з сябрамі!", "@@locale": "be", - "@@last_modified": "2026-01-20 12:31:06.570011", + "@@last_modified": "2026-01-21 10:42:45.087111", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11989,5 +11989,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Голас Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_bn.arb b/lib/l10n/intl_bn.arb index 28c180422..451b639ab 100644 --- a/lib/l10n/intl_bn.arb +++ b/lib/l10n/intl_bn.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:49.464406", + "@@last_modified": "2026-01-21 10:43:01.204800", "about": "সম্পর্কে", "@about": { "type": "String", @@ -11994,5 +11994,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "প্যাঙ্গিয়া বটের কণ্ঠ", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_bo.arb b/lib/l10n/intl_bo.arb index 800c24594..a87317500 100644 --- a/lib/l10n/intl_bo.arb +++ b/lib/l10n/intl_bo.arb @@ -4279,7 +4279,7 @@ "joinPublicTrip": "མི་ཚེས་ལ་ལོག་འབད།", "startOwnTrip": "ངེད་རང་གི་ལོག་ལ་སྦྱོར་བཅོས།", "@@locale": "bo", - "@@last_modified": "2026-01-20 12:31:44.969872", + "@@last_modified": "2026-01-21 10:42:59.058441", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -10644,5 +10644,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot voz", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ca.arb b/lib/l10n/intl_ca.arb index 5eb437241..7c6d2d7fa 100644 --- a/lib/l10n/intl_ca.arb +++ b/lib/l10n/intl_ca.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:09.126351", + "@@last_modified": "2026-01-21 10:42:46.152113", "about": "Quant a", "@about": { "type": "String", @@ -10914,5 +10914,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Veu del bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_cs.arb b/lib/l10n/intl_cs.arb index 321734d12..fef398790 100644 --- a/lib/l10n/intl_cs.arb +++ b/lib/l10n/intl_cs.arb @@ -1,6 +1,6 @@ { "@@locale": "cs", - "@@last_modified": "2026-01-20 12:31:00.323945", + "@@last_modified": "2026-01-21 10:42:42.984636", "about": "O aplikaci", "@about": { "type": "String", @@ -11497,5 +11497,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Hlas Pangea Bota", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_da.arb b/lib/l10n/intl_da.arb index 372a6e7c8..46dbe3d2c 100644 --- a/lib/l10n/intl_da.arb +++ b/lib/l10n/intl_da.arb @@ -1930,7 +1930,7 @@ "playWithAI": "Leg med AI for nu", "courseStartDesc": "Pangea Bot er klar til at starte når som helst!\n\n...men læring er bedre med venner!", "@@locale": "da", - "@@last_modified": "2026-01-20 12:30:10.761209", + "@@last_modified": "2026-01-21 10:42:22.819549", "@aboutHomeserver": { "type": "String", "placeholders": { @@ -11951,5 +11951,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot stemme", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 634b76211..2f9214e0b 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,6 @@ { "@@locale": "de", - "@@last_modified": "2026-01-20 12:30:47.434524", + "@@last_modified": "2026-01-21 10:42:37.466783", "alwaysUse24HourFormat": "true", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." @@ -10897,5 +10897,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot Stimme", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_el.arb b/lib/l10n/intl_el.arb index 176fb6f07..b90f11cdb 100644 --- a/lib/l10n/intl_el.arb +++ b/lib/l10n/intl_el.arb @@ -4456,7 +4456,7 @@ "playWithAI": "Παίξτε με την Τεχνητή Νοημοσύνη προς το παρόν", "courseStartDesc": "Ο Pangea Bot είναι έτοιμος να ξεκινήσει οποιαδήποτε στιγμή!\n\n...αλλά η μάθηση είναι καλύτερη με φίλους!", "@@locale": "el", - "@@last_modified": "2026-01-20 12:31:59.503296", + "@@last_modified": "2026-01-21 10:43:05.204211", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11948,5 +11948,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Φωνή Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index abc4a3d63..e90dfca6b 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -5056,5 +5056,6 @@ "constructUseIncGEDesc": "Incorrect grammar error practice", "fillInBlank": "Fill in the blank with the correct choice", "learn": "Learn", - "languageUpdated": "Target language updated!" + "languageUpdated": "Target language updated!", + "voiceDropdownTitle": "Pangea Bot voice" } diff --git a/lib/l10n/intl_eo.arb b/lib/l10n/intl_eo.arb index 5914ed46b..942654f9f 100644 --- a/lib/l10n/intl_eo.arb +++ b/lib/l10n/intl_eo.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:32:07.490560", + "@@last_modified": "2026-01-21 10:43:08.060333", "about": "Prio", "@about": { "type": "String", @@ -11979,5 +11979,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voĉo de Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 144e71870..4e77d447b 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,6 @@ { "@@locale": "es", - "@@last_modified": "2026-01-20 12:29:59.898184", + "@@last_modified": "2026-01-21 10:42:19.343875", "about": "Acerca de", "@about": { "type": "String", @@ -8124,5 +8124,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voz del bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_et.arb b/lib/l10n/intl_et.arb index 5d4e3f6c2..30c19ef49 100644 --- a/lib/l10n/intl_et.arb +++ b/lib/l10n/intl_et.arb @@ -1,6 +1,6 @@ { "@@locale": "et", - "@@last_modified": "2026-01-20 12:30:45.162738", + "@@last_modified": "2026-01-21 10:42:36.742735", "about": "Rakenduse teave", "@about": { "type": "String", @@ -11161,5 +11161,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Boti hääl", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_eu.arb b/lib/l10n/intl_eu.arb index 225d963e7..d913d4db3 100644 --- a/lib/l10n/intl_eu.arb +++ b/lib/l10n/intl_eu.arb @@ -1,6 +1,6 @@ { "@@locale": "eu", - "@@last_modified": "2026-01-20 12:30:40.144354", + "@@last_modified": "2026-01-21 10:42:34.277635", "about": "Honi buruz", "@about": { "type": "String", @@ -10890,5 +10890,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot ahotsa", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fa.arb b/lib/l10n/intl_fa.arb index 977dd42b8..6e395bb02 100644 --- a/lib/l10n/intl_fa.arb +++ b/lib/l10n/intl_fa.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:52.231221", + "@@last_modified": "2026-01-21 10:43:02.232868", "repeatPassword": "تکرار رمزعبور", "@repeatPassword": {}, "about": "درباره", @@ -11622,5 +11622,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "صدای ربات پانژیا", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fi.arb b/lib/l10n/intl_fi.arb index 6184adfec..707872200 100644 --- a/lib/l10n/intl_fi.arb +++ b/lib/l10n/intl_fi.arb @@ -4009,7 +4009,7 @@ "playWithAI": "Leiki tekoälyn kanssa nyt", "courseStartDesc": "Pangea Bot on valmis milloin tahansa!\n\n...mutta oppiminen on parempaa ystävien kanssa!", "@@locale": "fi", - "@@last_modified": "2026-01-20 12:30:08.099637", + "@@last_modified": "2026-01-21 10:42:21.857816", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11513,5 +11513,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Botin ääni", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fil.arb b/lib/l10n/intl_fil.arb index 986aa4015..a51993f5e 100644 --- a/lib/l10n/intl_fil.arb +++ b/lib/l10n/intl_fil.arb @@ -2787,7 +2787,7 @@ "selectAll": "Piliin lahat", "deselectAll": "Huwag piliin lahat", "@@locale": "fil", - "@@last_modified": "2026-01-20 12:31:19.884620", + "@@last_modified": "2026-01-21 10:42:50.349708", "@setCustomPermissionLevel": { "type": "String", "placeholders": {} @@ -11866,5 +11866,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Boses ng Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index c279297f0..58d082c74 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,6 +1,6 @@ { "@@locale": "fr", - "@@last_modified": "2026-01-20 12:32:20.398562", + "@@last_modified": "2026-01-21 10:43:12.775361", "about": "À propos", "@about": { "type": "String", @@ -11214,5 +11214,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voix du bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ga.arb b/lib/l10n/intl_ga.arb index c923a3bbd..b6e3263eb 100644 --- a/lib/l10n/intl_ga.arb +++ b/lib/l10n/intl_ga.arb @@ -4517,7 +4517,7 @@ "playWithAI": "Imir le AI faoi láthair", "courseStartDesc": "Tá Bot Pangea réidh chun dul am ar bith!\n\n...ach is fearr foghlaim le cairde!", "@@locale": "ga", - "@@last_modified": "2026-01-20 12:32:18.033824", + "@@last_modified": "2026-01-21 10:43:11.760663", "@customReaction": { "type": "String", "placeholders": {} @@ -10888,5 +10888,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "guth Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_gl.arb b/lib/l10n/intl_gl.arb index 3ebc2943d..28655e165 100644 --- a/lib/l10n/intl_gl.arb +++ b/lib/l10n/intl_gl.arb @@ -1,6 +1,6 @@ { "@@locale": "gl", - "@@last_modified": "2026-01-20 12:30:05.234280", + "@@last_modified": "2026-01-21 10:42:20.142432", "about": "Acerca de", "@about": { "type": "String", @@ -10887,5 +10887,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voz do bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_he.arb b/lib/l10n/intl_he.arb index 7ad96af48..f36caa914 100644 --- a/lib/l10n/intl_he.arb +++ b/lib/l10n/intl_he.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:30:31.884050", + "@@last_modified": "2026-01-21 10:42:30.598205", "about": "אודות", "@about": { "type": "String", @@ -11939,5 +11939,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "קול של פנגיאה בוט", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hi.arb b/lib/l10n/intl_hi.arb index a465a07b2..611db19dd 100644 --- a/lib/l10n/intl_hi.arb +++ b/lib/l10n/intl_hi.arb @@ -4483,7 +4483,7 @@ "playWithAI": "अभी के लिए एआई के साथ खेलें", "courseStartDesc": "पैंजिया बॉट कभी भी जाने के लिए तैयार है!\n\n...लेकिन दोस्तों के साथ सीखना बेहतर है!", "@@locale": "hi", - "@@last_modified": "2026-01-20 12:32:05.240015", + "@@last_modified": "2026-01-21 10:43:07.054066", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11975,5 +11975,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "पैंगिया बॉट की आवाज़", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hr.arb b/lib/l10n/intl_hr.arb index 64f6cccf8..17ea4d22e 100644 --- a/lib/l10n/intl_hr.arb +++ b/lib/l10n/intl_hr.arb @@ -1,6 +1,6 @@ { "@@locale": "hr", - "@@last_modified": "2026-01-20 12:30:29.823787", + "@@last_modified": "2026-01-21 10:42:29.581714", "about": "Informacije", "@about": { "type": "String", @@ -11262,5 +11262,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot glas", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hu.arb b/lib/l10n/intl_hu.arb index 8d543fa7e..490d3b22f 100644 --- a/lib/l10n/intl_hu.arb +++ b/lib/l10n/intl_hu.arb @@ -1,6 +1,6 @@ { "@@locale": "hu", - "@@last_modified": "2026-01-20 12:30:13.762355", + "@@last_modified": "2026-01-21 10:42:24.298395", "about": "Névjegy", "@about": { "type": "String", @@ -10891,5 +10891,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot hang", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ia.arb b/lib/l10n/intl_ia.arb index 4fda86d9e..54974eb51 100644 --- a/lib/l10n/intl_ia.arb +++ b/lib/l10n/intl_ia.arb @@ -1958,7 +1958,7 @@ "playWithAI": "Joca con le IA pro ora", "courseStartDesc": "Pangea Bot es preste a comenzar a qualunque momento!\n\n...ma apprender es melior con amicos!", "@@locale": "ia", - "@@last_modified": "2026-01-20 12:30:35.012898", + "@@last_modified": "2026-01-21 10:42:31.403920", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11968,5 +11968,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voix du bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_id.arb b/lib/l10n/intl_id.arb index 50917db2e..2d02858ce 100644 --- a/lib/l10n/intl_id.arb +++ b/lib/l10n/intl_id.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:30:15.788809", + "@@last_modified": "2026-01-21 10:42:25.871973", "setAsCanonicalAlias": "Atur sebagai alias utama", "@setAsCanonicalAlias": { "type": "String", @@ -10881,5 +10881,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Suara Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ie.arb b/lib/l10n/intl_ie.arb index 17b5245cf..caf59b6b4 100644 --- a/lib/l10n/intl_ie.arb +++ b/lib/l10n/intl_ie.arb @@ -4372,7 +4372,7 @@ "playWithAI": "Joca con AI pro ora", "courseStartDesc": "Pangea Bot es preste a partir a qualunque momento!\n\n...ma apprender es melior con amicos!", "@@locale": "ie", - "@@last_modified": "2026-01-20 12:30:26.700297", + "@@last_modified": "2026-01-21 10:42:28.431424", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11864,5 +11864,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot guth", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 5c8d17791..9284e09b3 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:30:55.791406", + "@@last_modified": "2026-01-21 10:42:40.690823", "about": "Informazioni", "@about": { "type": "String", @@ -10893,5 +10893,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voce del bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ja.arb b/lib/l10n/intl_ja.arb index 30d520729..c83be27b4 100644 --- a/lib/l10n/intl_ja.arb +++ b/lib/l10n/intl_ja.arb @@ -1,6 +1,6 @@ { "@@locale": "ja", - "@@last_modified": "2026-01-20 12:32:02.883097", + "@@last_modified": "2026-01-21 10:43:06.128364", "about": "このアプリについて", "@about": { "type": "String", @@ -11680,5 +11680,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "パンゲアボットの声", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ka.arb b/lib/l10n/intl_ka.arb index 8026eff56..d8e600706 100644 --- a/lib/l10n/intl_ka.arb +++ b/lib/l10n/intl_ka.arb @@ -2594,7 +2594,7 @@ "playWithAI": "ამ დროისთვის ითამაშეთ AI-თან", "courseStartDesc": "Pangea Bot მზადაა ნებისმიერ დროს გასასვლელად!\n\n...მაგრამ სწავლა უკეთესია მეგობრებთან ერთად!", "@@locale": "ka", - "@@last_modified": "2026-01-20 12:32:12.611848", + "@@last_modified": "2026-01-21 10:43:09.701292", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11920,5 +11920,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "პანჯეა ბოტის ხმა", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ko.arb b/lib/l10n/intl_ko.arb index f711d1304..ed5289391 100644 --- a/lib/l10n/intl_ko.arb +++ b/lib/l10n/intl_ko.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:29:56.840054", + "@@last_modified": "2026-01-21 10:42:17.603097", "about": "소개", "@about": { "type": "String", @@ -10998,5 +10998,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "판게아 봇 음성", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_lt.arb b/lib/l10n/intl_lt.arb index 888944382..35ca44e54 100644 --- a/lib/l10n/intl_lt.arb +++ b/lib/l10n/intl_lt.arb @@ -3861,7 +3861,7 @@ "playWithAI": "Žaiskite su dirbtiniu intelektu dabar", "courseStartDesc": "Pangea botas pasiruošęs bet kada pradėti!\n\n...bet mokymasis yra geresnis su draugais!", "@@locale": "lt", - "@@last_modified": "2026-01-20 12:31:36.164653", + "@@last_modified": "2026-01-21 10:42:55.447627", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11695,5 +11695,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot balsas", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_lv.arb b/lib/l10n/intl_lv.arb index 79a2fd834..64bbaf040 100644 --- a/lib/l10n/intl_lv.arb +++ b/lib/l10n/intl_lv.arb @@ -4482,7 +4482,7 @@ "playWithAI": "Tagad spēlējiet ar AI", "courseStartDesc": "Pangea bots ir gatavs jebkurā laikā!\n\n...bet mācīties ir labāk ar draugiem!", "@@locale": "lv", - "@@last_modified": "2026-01-20 12:31:22.453166", + "@@last_modified": "2026-01-21 10:42:51.147364", "analyticsInactiveTitle": "Pieprasījumi neaktīviem lietotājiem nevar tikt nosūtīti", "analyticsInactiveDesc": "Neaktīvi lietotāji, kuri nav pieteikušies kopš šīs funkcijas ieviešanas, neredzēs jūsu pieprasījumu.\n\nPieprasījuma poga parādīsies, kad viņi atgriezīsies. Jūs varat atkārtoti nosūtīt pieprasījumu vēlāk, noklikšķinot uz pieprasījuma pogas viņu vārdā, kad tā būs pieejama.", "accessRequestedTitle": "Pieprasījums piekļūt analītikai", @@ -10876,5 +10876,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot balss", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_nb.arb b/lib/l10n/intl_nb.arb index 8d8592bdb..5615f0550 100644 --- a/lib/l10n/intl_nb.arb +++ b/lib/l10n/intl_nb.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:02.821968", + "@@last_modified": "2026-01-21 10:42:43.891386", "about": "Om", "@about": { "type": "String", @@ -11983,5 +11983,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot-stemme", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_nl.arb b/lib/l10n/intl_nl.arb index 755841dc9..b021f9ad1 100644 --- a/lib/l10n/intl_nl.arb +++ b/lib/l10n/intl_nl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:42.507524", + "@@last_modified": "2026-01-21 10:42:58.326124", "about": "Over ons", "@about": { "type": "String", @@ -10890,5 +10890,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot stem", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 48ebf5396..9e3d32b91 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,6 @@ { "@@locale": "pl", - "@@last_modified": "2026-01-20 12:31:54.796841", + "@@last_modified": "2026-01-21 10:43:03.257336", "about": "O aplikacji", "@about": { "type": "String", @@ -10888,5 +10888,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Głos bota Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 1ac41d131..f80792528 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:30:42.601068", + "@@last_modified": "2026-01-21 10:42:35.392050", "copiedToClipboard": "Copiada para a área de transferência", "@copiedToClipboard": { "type": "String", @@ -11990,5 +11990,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voz do Bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt_BR.arb b/lib/l10n/intl_pt_BR.arb index da1140756..933c9f8f3 100644 --- a/lib/l10n/intl_pt_BR.arb +++ b/lib/l10n/intl_pt_BR.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:30:37.380939", + "@@last_modified": "2026-01-21 10:42:32.545549", "about": "Sobre", "@about": { "type": "String", @@ -11248,5 +11248,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voz do Bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt_PT.arb b/lib/l10n/intl_pt_PT.arb index c0a85355c..643c67fee 100644 --- a/lib/l10n/intl_pt_PT.arb +++ b/lib/l10n/intl_pt_PT.arb @@ -3331,7 +3331,7 @@ "selectAll": "Selecionar tudo", "deselectAll": "Desmarcar tudo", "@@locale": "pt_PT", - "@@last_modified": "2026-01-20 12:31:13.609297", + "@@last_modified": "2026-01-21 10:42:48.198355", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11919,5 +11919,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Voz do Bot Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ro.arb b/lib/l10n/intl_ro.arb index 5154c946d..7e868c193 100644 --- a/lib/l10n/intl_ro.arb +++ b/lib/l10n/intl_ro.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:30:21.408747", + "@@last_modified": "2026-01-21 10:42:26.609980", "about": "Despre", "@about": { "type": "String", @@ -11625,5 +11625,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Vocea Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index ca196e82b..e08d12d6e 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -1,6 +1,6 @@ { "@@locale": "ru", - "@@last_modified": "2026-01-20 12:32:09.850624", + "@@last_modified": "2026-01-21 10:43:08.936913", "about": "О проекте", "@about": { "type": "String", @@ -10995,5 +10995,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Голос бота Pangea", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sk.arb b/lib/l10n/intl_sk.arb index ceb204959..26dd291f9 100644 --- a/lib/l10n/intl_sk.arb +++ b/lib/l10n/intl_sk.arb @@ -1,6 +1,6 @@ { "@@locale": "sk", - "@@last_modified": "2026-01-20 12:30:24.498564", + "@@last_modified": "2026-01-21 10:42:27.675920", "about": "O aplikácii", "@about": { "type": "String", @@ -11974,5 +11974,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Hlas Pangea Bota", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sl.arb b/lib/l10n/intl_sl.arb index 9e4a1ff56..a9889e3a1 100644 --- a/lib/l10n/intl_sl.arb +++ b/lib/l10n/intl_sl.arb @@ -2464,7 +2464,7 @@ "playWithAI": "Za zdaj igrajte z AI-jem", "courseStartDesc": "Pangea Bot je pripravljen kadarkoli!\n\n...ampak je bolje učiti se s prijatelji!", "@@locale": "sl", - "@@last_modified": "2026-01-20 12:30:51.399294", + "@@last_modified": "2026-01-21 10:42:38.801732", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11971,5 +11971,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Glas Pangea Bota", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sr.arb b/lib/l10n/intl_sr.arb index fe98f2112..c32c01635 100644 --- a/lib/l10n/intl_sr.arb +++ b/lib/l10n/intl_sr.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:32:14.799697", + "@@last_modified": "2026-01-21 10:43:10.733270", "about": "О програму", "@about": { "type": "String", @@ -11992,5 +11992,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Glas Pangea Bota", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sv.arb b/lib/l10n/intl_sv.arb index 1d9224f70..9bffc0e88 100644 --- a/lib/l10n/intl_sv.arb +++ b/lib/l10n/intl_sv.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:57.066428", + "@@last_modified": "2026-01-21 10:43:04.367889", "about": "Om", "@about": { "type": "String", @@ -11368,5 +11368,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot röst", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ta.arb b/lib/l10n/intl_ta.arb index 42b73b4d5..5592467b6 100644 --- a/lib/l10n/intl_ta.arb +++ b/lib/l10n/intl_ta.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:40.562260", + "@@last_modified": "2026-01-21 10:42:57.399546", "acceptedTheInvitation": "👍 {username} அழைப்பை ஏற்றுக்கொண்டது", "@acceptedTheInvitation": { "type": "String", @@ -11114,5 +11114,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "பாஙேஆ பாட்டின் குரல்", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_te.arb b/lib/l10n/intl_te.arb index 3043221dc..9e1e5fe94 100644 --- a/lib/l10n/intl_te.arb +++ b/lib/l10n/intl_te.arb @@ -1920,7 +1920,7 @@ "playWithAI": "ఇప్పుడే AI తో ఆడండి", "courseStartDesc": "పాంజియా బాట్ ఎప్పుడైనా సిద్ధంగా ఉంటుంది!\n\n...కానీ స్నేహితులతో నేర్చుకోవడం మెరుగైనది!", "@@locale": "te", - "@@last_modified": "2026-01-20 12:31:32.903548", + "@@last_modified": "2026-01-21 10:42:54.652537", "@setCustomPermissionLevel": { "type": "String", "placeholders": {} @@ -11979,5 +11979,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "పాంజియా బాట్ శబ్దం", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_th.arb b/lib/l10n/intl_th.arb index ceb9ff50a..b07d69284 100644 --- a/lib/l10n/intl_th.arb +++ b/lib/l10n/intl_th.arb @@ -4456,7 +4456,7 @@ "playWithAI": "เล่นกับ AI ชั่วคราว", "courseStartDesc": "Pangea Bot พร้อมที่จะเริ่มต้นได้ทุกเมื่อ!\n\n...แต่การเรียนรู้ดีกว่ากับเพื่อน!", "@@locale": "th", - "@@last_modified": "2026-01-20 12:31:11.891533", + "@@last_modified": "2026-01-21 10:42:47.175182", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11948,5 +11948,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "เสียงของ Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_tr.arb b/lib/l10n/intl_tr.arb index 8d86e681c..fe247f360 100644 --- a/lib/l10n/intl_tr.arb +++ b/lib/l10n/intl_tr.arb @@ -1,6 +1,6 @@ { "@@locale": "tr", - "@@last_modified": "2026-01-20 12:31:28.469826", + "@@last_modified": "2026-01-21 10:42:53.424381", "about": "Hakkında", "@about": { "type": "String", @@ -11112,5 +11112,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot sesi", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_uk.arb b/lib/l10n/intl_uk.arb index b8330d92c..7734ef4fe 100644 --- a/lib/l10n/intl_uk.arb +++ b/lib/l10n/intl_uk.arb @@ -1,6 +1,6 @@ { "@@locale": "uk", - "@@last_modified": "2026-01-20 12:30:57.869011", + "@@last_modified": "2026-01-21 10:42:41.558170", "about": "Про застосунок", "@about": { "type": "String", @@ -10884,5 +10884,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Голос Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_vi.arb b/lib/l10n/intl_vi.arb index 69d334c03..0d9c741ab 100644 --- a/lib/l10n/intl_vi.arb +++ b/lib/l10n/intl_vi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:38.101874", + "@@last_modified": "2026-01-21 10:42:56.498587", "about": "Giới thiệu", "@about": { "type": "String", @@ -6460,5 +6460,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Giọng nói của Pangea Bot", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_yue.arb b/lib/l10n/intl_yue.arb index 69867a6fc..15558c19a 100644 --- a/lib/l10n/intl_yue.arb +++ b/lib/l10n/intl_yue.arb @@ -1856,7 +1856,7 @@ "selectAll": "全選", "deselectAll": "取消全選", "@@locale": "yue", - "@@last_modified": "2026-01-20 12:30:53.854617", + "@@last_modified": "2026-01-21 10:42:39.595731", "@ignoreUser": { "type": "String", "placeholders": {} @@ -11981,5 +11981,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot 聲音", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index 0953fb23e..e3b4d480b 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -1,6 +1,6 @@ { "@@locale": "zh", - "@@last_modified": "2026-01-20 12:31:47.017242", + "@@last_modified": "2026-01-21 10:43:00.180549", "about": "关于", "@about": { "type": "String", @@ -10881,5 +10881,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "潘吉亚机器人声音", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_zh_Hant.arb b/lib/l10n/intl_zh_Hant.arb index 26f055262..3491a8f78 100644 --- a/lib/l10n/intl_zh_Hant.arb +++ b/lib/l10n/intl_zh_Hant.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-20 12:31:16.140892", + "@@last_modified": "2026-01-21 10:42:49.437032", "about": "關於", "@about": { "type": "String", @@ -10888,5 +10888,10 @@ "@languageUpdated": { "type": "String", "placeholders": {} + }, + "voiceDropdownTitle": "Pangea Bot 語音", + "@voiceDropdownTitle": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index fe4f9bf1f..fc2ea9d32 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -13,8 +13,6 @@ import 'package:fluffychat/pages/chat/events/room_creation_state_event.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_roles_event_widget.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_summary_widget.dart'; -import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; -import 'package:fluffychat/pangea/bot/widgets/bot_settings_language_icon.dart'; import 'package:fluffychat/pangea/chat/extensions/custom_room_display_extension.dart'; import 'package:fluffychat/pangea/common/widgets/pressable_button.dart'; import 'package:fluffychat/pangea/common/widgets/shimmer_background.dart'; @@ -477,18 +475,6 @@ class Message extends StatelessWidget { presenceBackgroundColor: wallpaperMode ? Colors.transparent : null, - // #Pangea - miniIcon: - user.id == BotName.byEnvironment - ? BotSettingsLanguageIcon( - user: user, - ) - : null, - presenceOffset: - user.id == BotName.byEnvironment - ? const Offset(0, 0) - : null, - // Pangea# ); }, ), diff --git a/lib/pangea/activity_sessions/activity_participant_indicator.dart b/lib/pangea/activity_sessions/activity_participant_indicator.dart index 58fd0403f..61370cec7 100644 --- a/lib/pangea/activity_sessions/activity_participant_indicator.dart +++ b/lib/pangea/activity_sessions/activity_participant_indicator.dart @@ -6,8 +6,6 @@ import 'package:shimmer/shimmer.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; -import 'package:fluffychat/pangea/bot/widgets/bot_settings_language_icon.dart'; import 'package:fluffychat/utils/string_color.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/hover_builder.dart'; @@ -70,14 +68,6 @@ class ActivityParticipantIndicator extends StatelessWidget { name: userId!.localpart, size: 60.0, userId: userId, - miniIcon: - room != null && userId == BotName.byEnvironment - ? BotSettingsLanguageIcon(user: user!) - : null, - presenceOffset: - room != null && userId == BotName.byEnvironment - ? const Offset(0, 0) - : null, ) : ClipRRect( borderRadius: BorderRadius.circular(30), diff --git a/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart b/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart deleted file mode 100644 index a748fdccc..000000000 --- a/lib/pangea/bot/widgets/bot_chat_settings_dialog.dart +++ /dev/null @@ -1,194 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:dropdown_button2/dropdown_button2.dart'; -import 'package:matrix/matrix.dart'; - -import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; -import 'package:fluffychat/pangea/bot/utils/bot_room_extension.dart'; -import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart'; -import 'package:fluffychat/pangea/chat_settings/widgets/language_level_dropdown.dart'; -import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/common/widgets/dropdown_text_button.dart'; -import 'package:fluffychat/pangea/languages/language_model.dart'; -import 'package:fluffychat/pangea/languages/p_language_store.dart'; -import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/p_language_dropdown.dart'; -import 'package:fluffychat/widgets/matrix.dart'; - -class BotChatSettingsDialog extends StatefulWidget { - final Room room; - - const BotChatSettingsDialog({ - required this.room, - super.key, - }); - - @override - BotChatSettingsDialogState createState() => BotChatSettingsDialogState(); -} - -class BotChatSettingsDialogState extends State { - LanguageModel? _selectedLang; - LanguageLevelTypeEnum? _selectedLevel; - String? _selectedVoice; - - @override - void initState() { - final botSettings = widget.room.botOptions; - final activityPlan = _isActivity ? widget.room.activityPlan : null; - - _selectedLevel = activityPlan?.req.cefrLevel ?? botSettings?.languageLevel; - _selectedVoice = botSettings?.targetVoice; - final lang = - activityPlan?.req.targetLanguage ?? botSettings?.targetLanguage; - if (lang != null) { - _selectedLang = PLanguageStore.byLangCode(lang); - } - super.initState(); - } - - bool get _isActivity => widget.room.isActivitySession; - - Future _setLanguage(LanguageModel? lang) async { - setState(() { - _selectedLang = lang; - _selectedVoice = null; - }); - - final model = widget.room.botOptions ?? BotOptionsModel(); - model.targetLanguage = lang?.langCode; - model.targetVoice = null; - - try { - await widget.room.setBotOptions(model); - } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - 'roomId': widget.room.id, - 'langCode': lang?.langCode, - }, - ); - } - } - - Future _setLevel(LanguageLevelTypeEnum? level) async { - if (level == null) return; - - setState(() => _selectedLevel = level); - final model = widget.room.botOptions ?? BotOptionsModel(); - model.languageLevel = level; - try { - await widget.room.setBotOptions(model); - } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - 'roomId': widget.room.id, - 'level': level.name, - }, - ); - } - } - - Future _setVoice(String? voice) async { - setState(() => _selectedVoice = voice); - final model = widget.room.botOptions ?? BotOptionsModel(); - model.targetVoice = voice; - try { - await widget.room.setBotOptions(model); - } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - 'roomId': widget.room.id, - 'voice': voice, - }, - ); - } - } - - @override - Widget build(BuildContext context) { - return Material( - type: MaterialType.transparency, - child: Column( - spacing: 12.0, - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (widget.room.isActivitySession) - ListTile( - contentPadding: const EdgeInsets.all(0.0), - minLeadingWidth: 12.0, - leading: Icon( - Icons.info_outline, - size: 12.0, - color: Theme.of(context).disabledColor, - ), - title: Text( - L10n.of(context).activitySettingsOverrideWarning, - style: TextStyle( - color: Theme.of(context).disabledColor, - fontSize: 12.0, - ), - ), - ) - else - const SizedBox(), - PLanguageDropdown( - onChange: _setLanguage, - initialLanguage: _selectedLang, - languages: - MatrixState.pangeaController.pLanguageStore.targetOptions, - isL2List: true, - decorationText: L10n.of(context).targetLanguage, - enabled: !widget.room.isActivitySession, - ), - LanguageLevelDropdown( - initialLevel: _selectedLevel, - onChanged: _setLevel, - enabled: !widget.room.isActivitySession, - width: 300, - maxHeight: 300, - ), - DropdownButtonFormField2( - customButton: _selectedVoice != null - ? CustomDropdownTextButton(text: _selectedVoice!) - : null, - menuItemStyleData: const MenuItemStyleData( - padding: EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 16.0, - ), - ), - decoration: InputDecoration( - labelText: L10n.of(context).voice, - ), - isExpanded: true, - dropdownStyleData: DropdownStyleData( - maxHeight: 250, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(14.0), - ), - ), - items: (_selectedLang?.voices ?? []).map((voice) { - return DropdownMenuItem( - value: voice, - child: Text(voice), - ); - }).toList(), - onChanged: _setVoice, - value: _selectedVoice, - ), - const SizedBox(), - ], - ), - ); - } -} diff --git a/lib/pangea/bot/widgets/bot_settings_language_icon.dart b/lib/pangea/bot/widgets/bot_settings_language_icon.dart deleted file mode 100644 index 837bba59f..000000000 --- a/lib/pangea/bot/widgets/bot_settings_language_icon.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:matrix/matrix.dart'; - -import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; -import 'package:fluffychat/pangea/bot/utils/bot_room_extension.dart'; -import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/widgets/member_actions_popup_menu_button.dart'; - -class BotSettingsLanguageIcon extends StatelessWidget { - final User user; - - const BotSettingsLanguageIcon({ - super.key, - required this.user, - }); - - @override - Widget build(BuildContext context) { - final room = user.room; - String? langCode = room.botOptions?.targetLanguage; - if (room.isActivitySession && room.activityPlan != null) { - langCode = room.activityPlan!.req.targetLanguage; - } - if (langCode == null) { - return const SizedBox(); - } - - langCode = langCode.split('-').first; - return InkWell( - borderRadius: BorderRadius.circular(32.0), - onTap: room.isRoomAdmin - ? () => showMemberActionsPopupMenu( - context: context, - user: user, - room: room, - ) - : null, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16.0), - color: Theme.of(context).colorScheme.primaryContainer, - ), - padding: const EdgeInsets.symmetric(horizontal: 4.0), - child: Text( - langCode, - style: TextStyle( - fontSize: 10.0, - color: Theme.of(context).colorScheme.onPrimaryContainer, - fontWeight: FontWeight.bold, - ), - textAlign: TextAlign.center, - ), - ), - ); - } -} diff --git a/lib/pangea/chat_settings/models/bot_options_model.dart b/lib/pangea/chat_settings/models/bot_options_model.dart index 4108890d4..e21f141b9 100644 --- a/lib/pangea/chat_settings/models/bot_options_model.dart +++ b/lib/pangea/chat_settings/models/bot_options_model.dart @@ -8,23 +8,23 @@ import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dart'; class BotOptionsModel { - LanguageLevelTypeEnum languageLevel; - String topic; - List keywords; - bool safetyModeration; - String mode; - String? discussionTopic; - String? discussionKeywords; - bool? discussionTriggerReactionEnabled; - String? discussionTriggerReactionKey; - String? customSystemPrompt; - bool? customTriggerReactionEnabled; - String? customTriggerReactionKey; - String? textAdventureGameMasterInstructions; - String? targetLanguage; - String? targetVoice; + final LanguageLevelTypeEnum languageLevel; + final String topic; + final List keywords; + final bool safetyModeration; + final String mode; + final String? discussionTopic; + final String? discussionKeywords; + final bool? discussionTriggerReactionEnabled; + final String? discussionTriggerReactionKey; + final String? customSystemPrompt; + final bool? customTriggerReactionEnabled; + final String? customTriggerReactionKey; + final String? textAdventureGameMasterInstructions; + final String? targetLanguage; + final String? targetVoice; - BotOptionsModel({ + const BotOptionsModel({ //////////////////////////////////////////////////////////////////////////// // General Bot Options //////////////////////////////////////////////////////////////////////////// @@ -133,50 +133,45 @@ class BotOptionsModel { } } - //TODO: define enum with all possible values - updateBotOption(String key, dynamic value) { - switch (key) { - case ModelKey.languageLevel: - languageLevel = value; - break; - case ModelKey.safetyModeration: - safetyModeration = value; - break; - case ModelKey.mode: - mode = value; - break; - case ModelKey.discussionTopic: - discussionTopic = value; - break; - case ModelKey.discussionKeywords: - discussionKeywords = value; - break; - case ModelKey.discussionTriggerReactionEnabled: - discussionTriggerReactionEnabled = value; - break; - case ModelKey.discussionTriggerReactionKey: - discussionTriggerReactionKey = value; - break; - case ModelKey.customSystemPrompt: - customSystemPrompt = value; - break; - case ModelKey.customTriggerReactionEnabled: - customTriggerReactionEnabled = value; - break; - case ModelKey.customTriggerReactionKey: - customTriggerReactionKey = value; - break; - case ModelKey.textAdventureGameMasterInstructions: - textAdventureGameMasterInstructions = value; - break; - case ModelKey.targetLanguage: - targetLanguage = value; - break; - case ModelKey.targetVoice: - targetVoice = value; - break; - default: - throw Exception('Invalid key for bot options - $key'); - } + BotOptionsModel copyWith({ + LanguageLevelTypeEnum? languageLevel, + String? topic, + List? keywords, + bool? safetyModeration, + String? mode, + String? discussionTopic, + String? discussionKeywords, + bool? discussionTriggerReactionEnabled, + String? discussionTriggerReactionKey, + String? customSystemPrompt, + bool? customTriggerReactionEnabled, + String? customTriggerReactionKey, + String? textAdventureGameMasterInstructions, + String? targetLanguage, + String? targetVoice, + }) { + return BotOptionsModel( + languageLevel: languageLevel ?? this.languageLevel, + topic: topic ?? this.topic, + keywords: keywords ?? this.keywords, + safetyModeration: safetyModeration ?? this.safetyModeration, + mode: mode ?? this.mode, + discussionTopic: discussionTopic ?? this.discussionTopic, + discussionKeywords: discussionKeywords ?? this.discussionKeywords, + discussionTriggerReactionEnabled: discussionTriggerReactionEnabled ?? + this.discussionTriggerReactionEnabled, + discussionTriggerReactionKey: + discussionTriggerReactionKey ?? this.discussionTriggerReactionKey, + customSystemPrompt: customSystemPrompt ?? this.customSystemPrompt, + customTriggerReactionEnabled: + customTriggerReactionEnabled ?? this.customTriggerReactionEnabled, + customTriggerReactionKey: + customTriggerReactionKey ?? this.customTriggerReactionKey, + textAdventureGameMasterInstructions: + textAdventureGameMasterInstructions ?? + this.textAdventureGameMasterInstructions, + targetLanguage: targetLanguage ?? this.targetLanguage, + targetVoice: targetVoice ?? this.targetVoice, + ); } } diff --git a/lib/pangea/chat_settings/utils/bot_client_extension.dart b/lib/pangea/chat_settings/utils/bot_client_extension.dart index 6fbb1c636..5021d19ed 100644 --- a/lib/pangea/chat_settings/utils/bot_client_extension.dart +++ b/lib/pangea/chat_settings/utils/bot_client_extension.dart @@ -7,23 +7,19 @@ import 'package:fluffychat/pangea/chat/constants/default_power_level.dart'; import 'package:fluffychat/pangea/chat_settings/constants/bot_mode.dart'; import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; +import 'package:fluffychat/pangea/user/user_model.dart'; import 'package:fluffychat/widgets/matrix.dart'; extension BotClientExtension on Client { bool get hasBotDM => rooms.any((r) => r.isBotDM); + Room? get botDM => rooms.firstWhereOrNull((r) => r.isBotDM); - Room? get botDM => rooms.firstWhereOrNull( - (room) { - if (room.isDirectChat && - room.directChatMatrixID == BotName.byEnvironment) { - return true; - } - if (room.botOptions?.mode == BotMode.directChat) { - return true; - } - return false; - }, - ); + // All 2-member rooms with the bot + List get targetBotChats => rooms.where((r) { + if (r.isBotDM) return true; + if (r.summary.mJoinedMemberCount != 2) return false; + return r.getParticipants().any((u) => u.id == BotName.byEnvironment); + }).toList(); Future startChatWithBot() => startDirectChat( BotName.byEnvironment, @@ -45,27 +41,31 @@ extension BotClientExtension on Client { ], ); - Future updateBotOptions() async { - if (!isLogged() || botDM == null) return; + Future updateBotOptions(UserSettings userSettings) async { + final rooms = targetBotChats; + if (rooms.isEmpty) return; - final targetLanguage = - MatrixState.pangeaController.userController.userL2?.langCode; - final cefrLevel = MatrixState - .pangeaController.userController.profile.userSettings.cefrLevel; - final updateBotOptions = botDM!.botOptions ?? BotOptionsModel(); + final futures = []; + for (final room in rooms) { + final botOptions = room.botOptions ?? const BotOptionsModel(); + final targetLanguage = userSettings.targetLanguage; + final languageLevel = userSettings.cefrLevel; + final voice = userSettings.voice; - if (updateBotOptions.targetLanguage == targetLanguage && - updateBotOptions.languageLevel == cefrLevel) { - return; + if (botOptions.targetLanguage == targetLanguage && + botOptions.languageLevel == languageLevel && + botOptions.targetVoice == voice) { + continue; + } + + final updated = botOptions.copyWith( + targetLanguage: targetLanguage, + languageLevel: languageLevel, + targetVoice: voice, + ); + futures.add(room.setBotOptions(updated)); } - if (targetLanguage != null && - updateBotOptions.targetLanguage != targetLanguage) { - updateBotOptions.targetVoice = null; - } - - updateBotOptions.targetLanguage = targetLanguage; - updateBotOptions.languageLevel = cefrLevel; - await botDM!.setBotOptions(updateBotOptions); + await Future.wait(futures); } } diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index 5257753d3..e90d6d9a9 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -117,8 +117,9 @@ class PangeaController { userController.languageStream.stream.listen(_onLanguageUpdate); _settingsSubscription?.cancel(); - _settingsSubscription = userController.settingsUpdateStream.stream - .listen((_) => matrixState.client.updateBotOptions()); + _settingsSubscription = userController.settingsUpdateStream.stream.listen( + (update) => matrixState.client.updateBotOptions(update.userSettings), + ); _joinSpaceSubscription?.cancel(); _joinSpaceSubscription ??= matrixState.client.onSync.stream @@ -176,7 +177,7 @@ class PangeaController { } _clearCache(exclude: exclude); - matrixState.client.updateBotOptions(); + matrixState.client.updateBotOptions(userController.profile.userSettings); } static final List _storageKeys = [ diff --git a/lib/pangea/learning_settings/p_settings_switch_list_tile.dart b/lib/pangea/learning_settings/p_settings_switch_list_tile.dart index 188debae1..a0e928def 100644 --- a/lib/pangea/learning_settings/p_settings_switch_list_tile.dart +++ b/lib/pangea/learning_settings/p_settings_switch_list_tile.dart @@ -44,7 +44,6 @@ class PSettingsSwitchListTileState @override Widget build(BuildContext context) { return SwitchListTile.adaptive( - contentPadding: EdgeInsets.zero, value: currentValue, title: Text(widget.title), activeThumbColor: AppConfig.activeToggleColor, diff --git a/lib/pangea/learning_settings/settings_learning.dart b/lib/pangea/learning_settings/settings_learning.dart index f3e5f8765..d9bdcfa69 100644 --- a/lib/pangea/learning_settings/settings_learning.dart +++ b/lib/pangea/learning_settings/settings_learning.dart @@ -41,7 +41,6 @@ class SettingsLearningController extends State { PangeaController pangeaController = MatrixState.pangeaController; late Profile _profile; - final GlobalKey formKey = GlobalKey(); String? languageMatchError; final ScrollController scrollController = ScrollController(); @@ -110,18 +109,16 @@ class SettingsLearningController extends State { updateToolSetting(ToolSetting.enableTTS, false); } - if (formKey.currentState!.validate()) { - await showFutureLoadingDialog( - context: context, - future: () async => pangeaController.userController - .updateProfile( - (_) => _profile, - waitForDataInSync: true, - ) - .timeout(const Duration(seconds: 15)), - ); - Navigator.of(context).pop(); - } + await showFutureLoadingDialog( + context: context, + future: () async => pangeaController.userController + .updateProfile( + (_) => _profile, + waitForDataInSync: true, + ) + .timeout(const Duration(seconds: 15)), + ); + Navigator.of(context).pop(); } Future resetInstructionTooltips() async { @@ -153,11 +150,12 @@ class SettingsLearningController extends State { LanguageModel? sourceLanguage, LanguageModel? targetLanguage, }) async { - if (sourceLanguage != null) { + if (sourceLanguage != null && sourceLanguage != selectedSourceLanguage) { _profile.userSettings.sourceLanguage = sourceLanguage.langCode; } - if (targetLanguage != null) { + if (targetLanguage != null && targetLanguage != selectedTargetLanguage) { _profile.userSettings.targetLanguage = targetLanguage.langCode; + _profile.userSettings.voice = null; if (!_profile.toolSettings.enableTTS && isTTSSupported) { updateToolSetting(ToolSetting.enableTTS, true); } @@ -181,6 +179,11 @@ class SettingsLearningController extends State { if (mounted) setState(() {}); } + void setVoice(String? voice) { + _profile.userSettings.voice = voice; + if (mounted) setState(() {}); + } + void changeCountry(Country? country) { _profile.userSettings.country = country?.name; if (mounted) setState(() {}); @@ -343,6 +346,8 @@ class SettingsLearningController extends State { LanguageLevelTypeEnum get cefrLevel => _profile.userSettings.cefrLevel; + String? get selectedVoice => _profile.userSettings.voice; + Country? get country => CountryService().findByName(_profile.userSettings.country); diff --git a/lib/pangea/learning_settings/settings_learning_view.dart b/lib/pangea/learning_settings/settings_learning_view.dart index d6037fc87..1f11f2872 100644 --- a/lib/pangea/learning_settings/settings_learning_view.dart +++ b/lib/pangea/learning_settings/settings_learning_view.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/chat_settings/widgets/language_level_dropdown.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; @@ -14,6 +13,7 @@ import 'package:fluffychat/pangea/learning_settings/p_language_dropdown.dart'; import 'package:fluffychat/pangea/learning_settings/p_settings_switch_list_tile.dart'; import 'package:fluffychat/pangea/learning_settings/settings_learning.dart'; import 'package:fluffychat/pangea/learning_settings/tool_settings_enum.dart'; +import 'package:fluffychat/pangea/learning_settings/voice_dropdown.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -45,21 +45,23 @@ class SettingsLearningView extends StatelessWidget { ) : null, ), - body: Form( - key: controller.formKey, - child: ListTileTheme( - iconColor: Theme.of(context).textTheme.bodyLarge!.color, - child: MaxWidthBody( - withScrolling: false, - child: Column( - children: [ - Expanded( - child: SingleChildScrollView( - controller: controller.scrollController, + body: ListTileTheme( + iconColor: Theme.of(context).textTheme.bodyLarge!.color, + child: MaxWidthBody( + withScrolling: false, + child: Column( + children: [ + Expanded( + child: SingleChildScrollView( + controller: controller.scrollController, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 32.0), child: Column( + spacing: 16.0, children: [ Padding( - padding: const EdgeInsets.all(16.0), + padding: + const EdgeInsets.symmetric(horizontal: 16.0), child: Column( spacing: 16.0, children: [ @@ -99,171 +101,112 @@ class SettingsLearningView extends StatelessWidget { .colorScheme .surfaceContainerHigh, ), - AnimatedSize( - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - child: controller.userL1?.langCodeShort == - controller.userL2?.langCodeShort - ? Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, + if (controller.userL1?.langCodeShort == + controller.userL2?.langCodeShort) + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + ), + child: Row( + spacing: 8.0, + children: [ + Icon( + Icons.info_outlined, + color: Theme.of(context) + .colorScheme + .error, + ), + Flexible( + child: Text( + L10n.of(context) + .noIdenticalLanguages, + style: TextStyle( + color: Theme.of(context) + .colorScheme + .error, + ), ), - child: Row( - spacing: 8.0, - children: [ - Icon( - Icons.info_outlined, - color: Theme.of(context) - .colorScheme - .error, - ), - Flexible( - child: Text( - L10n.of(context) - .noIdenticalLanguages, - style: TextStyle( - color: Theme.of(context) - .colorScheme - .error, - ), - ), - ), - ], - ), - ) - : const SizedBox.shrink(), - ), - CountryPickerDropdown(controller), + ), + ], + ), + ), LanguageLevelDropdown( initialLevel: controller.cefrLevel, onChanged: controller.setCefrLevel, ), + VoiceDropdown( + value: controller.selectedVoice, + language: controller.selectedTargetLanguage, + onChanged: controller.setVoice, + ), + CountryPickerDropdown(controller), GenderDropdown( initialGender: controller.gender, onChanged: controller.setGender, ), - Container( - decoration: BoxDecoration( - border: Border.all( - color: Colors.white54, - ), - borderRadius: BorderRadius.circular(8.0), - ), - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - ProfileSettingsSwitchListTile.adaptive( - defaultValue: - controller.getToolSetting( - ToolSetting.autoIGC, - ), - title: ToolSetting.autoIGC - .toolName(context), - subtitle: ToolSetting.autoIGC - .toolDescription(context), - onChange: (bool value) => - controller.updateToolSetting( - ToolSetting.autoIGC, - value, - ), - enabled: true, - ), - ProfileSettingsSwitchListTile.adaptive( - defaultValue: - controller.getToolSetting( - ToolSetting.enableAutocorrect, - ), - title: ToolSetting.enableAutocorrect - .toolName(context), - subtitle: ToolSetting - .enableAutocorrect - .toolDescription(context), - onChange: (bool value) { - controller.updateToolSetting( - ToolSetting.enableAutocorrect, - value, - ); - if (value) { - controller - .showKeyboardSettingsDialog(); - } - }, - enabled: true, - ), - ], - ), - ), - for (final toolSetting - in ToolSetting.values.where( - (tool) => - tool.isAvailableSetting && - tool != ToolSetting.autoIGC && - tool != ToolSetting.enableAutocorrect, - )) - Column( - children: [ - ProfileSettingsSwitchListTile.adaptive( - defaultValue: controller - .getToolSetting(toolSetting), - title: toolSetting.toolName(context), - subtitle: toolSetting == - ToolSetting.enableTTS && - !controller.isTTSSupported - ? null - : toolSetting - .toolDescription(context), - onChange: (bool value) => - controller.updateToolSetting( - toolSetting, - value, - ), - ), - ], - ), - SwitchListTile.adaptive( - value: controller.publicProfile, - onChanged: controller.setPublicProfile, - title: Text( - L10n.of(context).publicProfileTitle, - ), - subtitle: Text( - L10n.of(context).publicProfileDesc, - ), - activeThumbColor: - AppConfig.activeToggleColor, - contentPadding: EdgeInsets.zero, - ), - ResetInstructionsListTile( - controller: controller, - ), ], ), ), + ...ToolSetting.values + .where( + (tool) => tool.isAvailableSetting, + ) + .map( + (toolSetting) => _ProfileSwitchTile( + value: + controller.getToolSetting(toolSetting), + setting: toolSetting, + onChanged: (v) { + controller.updateToolSetting( + toolSetting, + v, + ); + if (v && + toolSetting == + ToolSetting.enableTTS) { + controller.showKeyboardSettingsDialog(); + } + }, + ), + ), + SwitchListTile.adaptive( + value: controller.publicProfile, + onChanged: controller.setPublicProfile, + title: Text( + L10n.of(context).publicProfileTitle, + ), + subtitle: Text( + L10n.of(context).publicProfileDesc, + ), + activeThumbColor: AppConfig.activeToggleColor, + ), + ResetInstructionsListTile( + controller: controller, + ), ], ), ), ), - Padding( - padding: const EdgeInsets.all(16.0), - child: SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: controller.haveSettingsBeenChanged - ? controller.submit - : null, - child: Text(L10n.of(context).saveChanges), - ), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: controller.haveSettingsBeenChanged + ? controller.submit + : null, + child: Text(L10n.of(context).saveChanges), ), ), - ], - ), + ), + ], ), ), ), ); if (!controller.widget.isDialog) return dialogContent; - return FullWidthDialog( dialogContent: dialogContent, maxWidth: 600, @@ -273,3 +216,25 @@ class SettingsLearningView extends StatelessWidget { ); } } + +class _ProfileSwitchTile extends StatelessWidget { + final bool value; + final ToolSetting setting; + final Function(bool) onChanged; + + const _ProfileSwitchTile({ + required this.value, + required this.setting, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + return ProfileSettingsSwitchListTile.adaptive( + defaultValue: value, + title: setting.toolName(context), + subtitle: setting.toolDescription(context), + onChange: onChanged, + ); + } +} diff --git a/lib/pangea/learning_settings/voice_dropdown.dart b/lib/pangea/learning_settings/voice_dropdown.dart new file mode 100644 index 000000000..4c6b77133 --- /dev/null +++ b/lib/pangea/learning_settings/voice_dropdown.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; + +import 'package:dropdown_button2/dropdown_button2.dart'; + +import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pangea/common/widgets/dropdown_text_button.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; + +class VoiceDropdown extends StatelessWidget { + final String? value; + final LanguageModel? language; + final Function(String?) onChanged; + + const VoiceDropdown({ + super.key, + this.value, + this.language, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + return DropdownButtonFormField2( + customButton: + value != null ? CustomDropdownTextButton(text: value!) : null, + menuItemStyleData: const MenuItemStyleData( + padding: EdgeInsets.symmetric( + vertical: 8.0, + horizontal: 16.0, + ), + ), + decoration: InputDecoration( + labelText: L10n.of(context).voiceDropdownTitle, + ), + isExpanded: true, + dropdownStyleData: DropdownStyleData( + maxHeight: 250, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainerHigh, + borderRadius: BorderRadius.circular(14.0), + ), + ), + items: (language?.voices ?? []).map((voice) { + return DropdownMenuItem( + value: voice, + child: Text(voice), + ); + }).toList(), + onChanged: onChanged, + value: value, + ); + } +} diff --git a/lib/pangea/toolbar/message_practice/message_audio_card.dart b/lib/pangea/toolbar/message_practice/message_audio_card.dart index bf57035aa..6a3661b61 100644 --- a/lib/pangea/toolbar/message_practice/message_audio_card.dart +++ b/lib/pangea/toolbar/message_practice/message_audio_card.dart @@ -8,10 +8,10 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/chat/events/audio_player.dart'; import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart'; -import 'package:fluffychat/pangea/bot/utils/bot_room_extension.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/text_to_speech/text_to_speech_response_model.dart'; +import 'package:fluffychat/widgets/matrix.dart'; class MessageAudioCard extends StatefulWidget { final PangeaMessageEvent messageEvent; @@ -44,7 +44,7 @@ class MessageAudioCardState extends State { try { audioFile = await widget.messageEvent.requestTextToSpeech( widget.messageEvent.messageDisplayLangCode, - widget.messageEvent.room.botOptions?.targetVoice, + MatrixState.pangeaController.userController.voice, ); debugPrint("audio file is now: $audioFile. setting starts and ends..."); if (mounted) setState(() => _isLoading = false); diff --git a/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart b/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart index a5f243674..f7abe6588 100644 --- a/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart +++ b/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart @@ -7,7 +7,6 @@ import 'package:matrix/matrix.dart'; import 'package:path_provider/path_provider.dart'; import 'package:fluffychat/pangea/analytics_misc/lemma_emoji_setter_mixin.dart'; -import 'package:fluffychat/pangea/bot/utils/bot_room_extension.dart'; import 'package:fluffychat/pangea/common/utils/async_state.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; @@ -58,7 +57,7 @@ class _AudioLoader extends AsyncLoader<(PangeaAudioFile, File?)> { Future<(PangeaAudioFile, File?)> fetch() async { final audioBytes = await messageEvent.requestTextToSpeech( messageEvent.messageDisplayLangCode, - messageEvent.room.botOptions?.targetVoice, + MatrixState.pangeaController.userController.voice, ); File? audioFile; diff --git a/lib/pangea/user/user_controller.dart b/lib/pangea/user/user_controller.dart index 43b9736ea..2e3be4226 100644 --- a/lib/pangea/user/user_controller.dart +++ b/lib/pangea/user/user_controller.dart @@ -431,6 +431,8 @@ class UserController { : langModel; } + String? get voice => profile.userSettings.voice; + bool get languagesSet => userL1Code != null && userL2Code != null && diff --git a/lib/pangea/user/user_model.dart b/lib/pangea/user/user_model.dart index 60ce1873a..ff0184627 100644 --- a/lib/pangea/user/user_model.dart +++ b/lib/pangea/user/user_model.dart @@ -19,6 +19,7 @@ class UserSettings { GenderEnum gender; String? country; LanguageLevelTypeEnum cefrLevel; + String? voice; UserSettings({ this.dateOfBirth, @@ -29,6 +30,7 @@ class UserSettings { this.gender = GenderEnum.unselected, this.country, this.cefrLevel = LanguageLevelTypeEnum.a1, + this.voice, }); factory UserSettings.fromJson(Map json) => UserSettings( @@ -52,6 +54,7 @@ class UserSettings { json[ModelKey.cefrLevel], ) : LanguageLevelTypeEnum.a1, + voice: json[ModelKey.voice], ); Map toJson() { @@ -64,6 +67,7 @@ class UserSettings { data[ModelKey.userGender] = gender.string; data[ModelKey.userCountry] = country; data[ModelKey.cefrLevel] = cefrLevel.string; + data[ModelKey.voice] = voice; return data; } @@ -123,6 +127,7 @@ class UserSettings { gender: gender, country: country, cefrLevel: cefrLevel, + voice: voice, ); } @@ -138,7 +143,8 @@ class UserSettings { other.sourceLanguage == sourceLanguage && other.gender == gender && other.country == country && - other.cefrLevel == cefrLevel; + other.cefrLevel == cefrLevel && + other.voice == voice; } @override @@ -151,6 +157,7 @@ class UserSettings { gender.hashCode, country.hashCode, cefrLevel.hashCode, + voice.hashCode, ]); } diff --git a/lib/widgets/avatar.dart b/lib/widgets/avatar.dart index bfba786b1..ae0e10471 100644 --- a/lib/widgets/avatar.dart +++ b/lib/widgets/avatar.dart @@ -28,7 +28,6 @@ class Avatar extends StatelessWidget { final double? presenceSize; final Offset? presenceOffset; - final Widget? miniIcon; // Pangea# const Avatar({ @@ -48,7 +47,6 @@ class Avatar extends StatelessWidget { this.userId, this.presenceSize, this.presenceOffset, - this.miniIcon, // Pangea# super.key, }); @@ -140,13 +138,7 @@ class Avatar extends StatelessWidget { ), // #Pangea // if (presenceUserId != null) - if (miniIcon != null) - Positioned( - bottom: presenceOffset?.dy ?? -3, - right: presenceOffset?.dx ?? -3, - child: miniIcon!, - ) - else if (presenceUserId != null && size >= 32.0 && showPresence) + if (presenceUserId != null && size >= 32.0 && showPresence) // Pangea# PresenceBuilder( client: client, diff --git a/lib/widgets/member_actions_popup_menu_button.dart b/lib/widgets/member_actions_popup_menu_button.dart index 75376e351..c80524b5a 100644 --- a/lib/widgets/member_actions_popup_menu_button.dart +++ b/lib/widgets/member_actions_popup_menu_button.dart @@ -6,8 +6,6 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_misc/level_display_name.dart'; import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; -import 'package:fluffychat/pangea/bot/widgets/bot_chat_settings_dialog.dart'; -import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/permission_slider_dialog.dart'; import 'adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; @@ -108,15 +106,15 @@ void showMemberActionsPopupMenu({ ], ), ), - if (user.id == BotName.byEnvironment && room != null && room.isRoomAdmin) - PopupMenuItem( - enabled: false, - padding: const EdgeInsets.only( - left: 12.0, - right: 12.0, - ), - child: BotChatSettingsDialog(room: room), - ), + // if (user.id == BotName.byEnvironment && room != null && room.isRoomAdmin) + // PopupMenuItem( + // enabled: false, + // padding: const EdgeInsets.only( + // left: 12.0, + // right: 12.0, + // ), + // child: BotChatSettingsDialog(room: room), + // ), const PopupMenuDivider(), // #Pangea if (user.room.client.userID != user.id) From ed1ade783aaa162cf5f83f5134b0b4530e065348 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 10:53:16 -0500 Subject: [PATCH 12/18] chore: Remove sentence-level pronunciation (#5306) --- .../toolbar/layout/overlay_message.dart | 29 +++++++++---------- .../select_mode_controller.dart | 3 -- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/lib/pangea/toolbar/layout/overlay_message.dart b/lib/pangea/toolbar/layout/overlay_message.dart index 2e0b134f3..13008833f 100644 --- a/lib/pangea/toolbar/layout/overlay_message.dart +++ b/lib/pangea/toolbar/layout/overlay_message.dart @@ -14,9 +14,6 @@ import 'package:fluffychat/pangea/common/utils/async_state.dart'; import 'package:fluffychat/pangea/common/widgets/error_indicator.dart'; import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; -import 'package:fluffychat/pangea/languages/language_model.dart'; -import 'package:fluffychat/pangea/languages/p_language_store.dart'; -import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_widget.dart'; import 'package:fluffychat/pangea/toolbar/layout/reading_assistance_mode_enum.dart'; import 'package:fluffychat/pangea/toolbar/message_selection_overlay.dart'; import 'package:fluffychat/pangea/toolbar/reading_assistance/select_mode_buttons.dart'; @@ -500,19 +497,19 @@ class _MessageBubbleTranscription extends StatelessWidget { onClick: onTokenSelected, isSelected: isTokenSelected, ), - if (MatrixState - .pangeaController.userController.showTranscription) - PhoneticTranscriptionWidget( - text: transcription.transcript.text, - textLanguage: PLanguageStore.byLangCode( - transcription.langCode, - ) ?? - LanguageModel.unknown, - style: style, - iconColor: style.color, - onTranscriptionFetched: () => - controller.contentChangedStream.add(true), - ), + // if (MatrixState + // .pangeaController.userController.showTranscription) + // PhoneticTranscriptionWidget( + // text: transcription.transcript.text, + // textLanguage: PLanguageStore.byLangCode( + // transcription.langCode, + // ) ?? + // LanguageModel.unknown, + // style: style, + // iconColor: style.color, + // onTranscriptionFetched: () => + // controller.contentChangedStream.add(true), + // ), ], ), ); diff --git a/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart b/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart index f7abe6588..d285fc1ce 100644 --- a/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart +++ b/lib/pangea/toolbar/reading_assistance/select_mode_controller.dart @@ -90,8 +90,6 @@ class SelectModeController with LemmaEmojiSetter { ValueNotifier selectedMode = ValueNotifier(null); - final StreamController contentChangedStream = StreamController.broadcast(); - // Sometimes the same token is clicked twice. Setting it to the same value // won't trigger the notifier, so use the bool for force it to trigger. ValueNotifier<(PangeaTokenText?, bool)> playTokenNotifier = @@ -104,7 +102,6 @@ class SelectModeController with LemmaEmojiSetter { _translationLoader.dispose(); _sttTranslationLoader.dispose(); _audioLoader.dispose(); - contentChangedStream.close(); } static List get _textModes => [ From ea0c1afb10bb0b0622134200a3318a704961e841 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 11:23:17 -0500 Subject: [PATCH 13/18] fix: use sync stream to update analytics requests indicator (#5307) --- .../analytics_request_indicator.dart | 41 ++++++++++++++----- .../analytics_requests_repo.dart | 4 ++ .../space_analytics/space_analytics.dart | 1 + 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/lib/pangea/space_analytics/analytics_request_indicator.dart b/lib/pangea/space_analytics/analytics_request_indicator.dart index 9ba4e910b..6b9d854a8 100644 --- a/lib/pangea/space_analytics/analytics_request_indicator.dart +++ b/lib/pangea/space_analytics/analytics_request_indicator.dart @@ -10,7 +10,6 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/space_analytics/space_analytics_requested_dialog.dart'; -import 'package:fluffychat/utils/stream_extension.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; class AnalyticsRequestIndicator extends StatefulWidget { @@ -60,18 +59,38 @@ class AnalyticsRequestIndicatorState extends State { ); await Future.wait(futures); - final analyicsRoomIds = analyticsRooms.map((r) => r.id).toSet(); + final analyticsRoomIds = analyticsRooms.map((r) => r.id).toSet(); _analyticsRoomSub?.cancel(); - _analyticsRoomSub = widget.room.client.onRoomState.stream - .where( - (event) => - analyicsRoomIds.contains(event.roomId) && - event.state.type == EventTypes.RoomMember, - ) - .rateLimit(const Duration(seconds: 1)) - .listen((_) => setState(() {})); + _analyticsRoomSub = widget.room.client.onSync.stream.listen((update) async { + final joined = update.rooms?.join?.entries + .where((e) => analyticsRoomIds.contains(e.key)); - if (mounted) setState(() {}); + if (joined == null || joined.isEmpty) return; + final Set updatedRoomIds = {}; + for (final entry in joined) { + final memberEvents = entry.value.timeline?.events?.where( + (e) => e.type == EventTypes.RoomMember, + ); + if (memberEvents != null && memberEvents.isNotEmpty) { + updatedRoomIds.add(entry.key); + } + } + + if (updatedRoomIds.isEmpty) return; + for (final roomId in updatedRoomIds) { + final room = widget.room.client.getRoomById(roomId); + if (room == null) continue; + await room.requestParticipants( + [Membership.join, Membership.invite, Membership.knock], + false, + true, + ); + } + + if (mounted) { + setState(() {}); + } + }); } Map> get _knockingAdmins { diff --git a/lib/pangea/space_analytics/analytics_requests_repo.dart b/lib/pangea/space_analytics/analytics_requests_repo.dart index ef6bfe1a5..2c3b39ffd 100644 --- a/lib/pangea/space_analytics/analytics_requests_repo.dart +++ b/lib/pangea/space_analytics/analytics_requests_repo.dart @@ -94,4 +94,8 @@ class AnalyticsRequestsRepo { final key = _storageKey(userId, language); await _requestStorage.remove(key); } + + static Future clear() async { + await _requestStorage.erase(); + } } diff --git a/lib/pangea/space_analytics/space_analytics.dart b/lib/pangea/space_analytics/space_analytics.dart index bef031cf5..5db3b2248 100644 --- a/lib/pangea/space_analytics/space_analytics.dart +++ b/lib/pangea/space_analytics/space_analytics.dart @@ -189,6 +189,7 @@ class SpaceAnalyticsState extends State { Future refresh() async { if (room == null || !room!.isSpace || selectedLanguage == null) return; + await AnalyticsRequestsRepo.clear(); setState(() { downloads = Map.fromEntries( From 2f5d67e2020c1341b8e4d78386507a31cf8177ee Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 13:23:52 -0500 Subject: [PATCH 14/18] fix: disable text scaling in learning progress indicators (#5313) --- lib/pangea/analytics_summary/learning_progress_indicators.dart | 2 ++ lib/pangea/analytics_summary/progress_indicator.dart | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/pangea/analytics_summary/learning_progress_indicators.dart b/lib/pangea/analytics_summary/learning_progress_indicators.dart index 8c7079d2c..885ffd0e0 100644 --- a/lib/pangea/analytics_summary/learning_progress_indicators.dart +++ b/lib/pangea/analytics_summary/learning_progress_indicators.dart @@ -147,6 +147,7 @@ class LearningProgressIndicators extends StatelessWidget { .colorScheme .primary, ), + textScaler: TextScaler.noScaling, ), if (userL1 != null && userL2 != null) const Icon(Icons.chevron_right_outlined), @@ -162,6 +163,7 @@ class LearningProgressIndicators extends StatelessWidget { .colorScheme .primary, ), + textScaler: TextScaler.noScaling, ), ], ), diff --git a/lib/pangea/analytics_summary/progress_indicator.dart b/lib/pangea/analytics_summary/progress_indicator.dart index 579ba205f..4a7ac798b 100644 --- a/lib/pangea/analytics_summary/progress_indicator.dart +++ b/lib/pangea/analytics_summary/progress_indicator.dart @@ -125,6 +125,7 @@ class AnimatedFloatingNumberState extends State Text( widget.number.toString(), style: indicatorStyle, + textScaler: TextScaler.noScaling, ), ], ); From 7458f8f55973513cb8f7939719e827753c5b1541 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 13:34:20 -0500 Subject: [PATCH 15/18] fix: don't auto-play bot audio message if another audio message is playing (#5315) --- lib/pages/chat/chat.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 215fb2063..58947f941 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -496,6 +496,8 @@ class ChatController extends State if (botAudioEvent == null) return; final matrix = Matrix.of(context); + if (matrix.voiceMessageEventId.value != null) return; + matrix.voiceMessageEventId.value = botAudioEvent.eventId; matrix.audioPlayer?.dispose(); matrix.audioPlayer = AudioPlayer(); From 1c6c2ee44a8f2d6c0ab6c9d2673f48c1ed5bc94b Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 13:45:36 -0500 Subject: [PATCH 16/18] fix: restrict when analytics practice session loss popup is shown (#5316) --- .../analytics_practice_page.dart | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/pangea/analytics_practice/analytics_practice_page.dart b/lib/pangea/analytics_practice/analytics_practice_page.dart index 9f3c0573e..6a7cc6fe5 100644 --- a/lib/pangea/analytics_practice/analytics_practice_page.dart +++ b/lib/pangea/analytics_practice/analytics_practice_page.dart @@ -58,7 +58,7 @@ class SessionLoader extends AsyncLoader { } class AnalyticsPractice extends StatefulWidget { - static bool bypassExitConfirmation = false; + static bool bypassExitConfirmation = true; final ConstructTypeEnum type; const AnalyticsPractice({ @@ -189,18 +189,18 @@ class AnalyticsPracticeState extends State String choiceTargetId(String choiceId) => '${widget.type.name}-choice-card-${choiceId.replaceAll(' ', '_')}'; - void _resetActivityState() { + void _clearState() { activityState.value = const AsyncState.loading(); activityTarget.value = null; enableChoicesNotifier.value = true; - } - void _resetSessionState() { progressNotifier.value = 0.0; _queue.clear(); _choiceTexts.clear(); _choiceEmojis.clear(); activityState.value = const AsyncState.idle(); + + AnalyticsPractice.bypassExitConfirmation = true; } void updateElapsedTime(int seconds) { @@ -227,8 +227,7 @@ class AnalyticsPracticeState extends State Future _onLanguageUpdate() async { try { - _resetActivityState(); - _resetSessionState(); + _clearState(); await _analyticsService .updateDispatcher.constructUpdateStream.stream.first .timeout(const Duration(seconds: 10)); @@ -252,9 +251,7 @@ class AnalyticsPracticeState extends State } Future reloadSession() async { - _resetActivityState(); - _resetSessionState(); - + _clearState(); _sessionLoader.reset(); await _startSession(); } @@ -294,8 +291,10 @@ class AnalyticsPracticeState extends State final activity = await nextActivityCompleter.completer.future; activityState.value = AsyncState.loaded(activity); + AnalyticsPractice.bypassExitConfirmation = false; } } catch (e) { + AnalyticsPractice.bypassExitConfirmation = true; activityState.value = AsyncState.error(e); } finally { _continuing = false; @@ -319,7 +318,9 @@ class AnalyticsPracticeState extends State if (!mounted) return; activityState.value = AsyncState.loaded(res); + AnalyticsPractice.bypassExitConfirmation = false; } catch (e) { + AnalyticsPractice.bypassExitConfirmation = true; if (!mounted) return; activityState.value = AsyncState.error(e); return; From 0d9982534e81f2aaaf6b1918e49d61a577af54c5 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 13:49:18 -0500 Subject: [PATCH 17/18] fix: hide info about course editing in join mode (#5317) --- .../course_creation/selected_course_view.dart | 63 ++++++++++--------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/lib/pangea/course_creation/selected_course_view.dart b/lib/pangea/course_creation/selected_course_view.dart index 6f54c0363..f94832370 100644 --- a/lib/pangea/course_creation/selected_course_view.dart +++ b/lib/pangea/course_creation/selected_course_view.dart @@ -234,40 +234,43 @@ class SelectedCourseView extends StatelessWidget { spacing: 8.0, mainAxisSize: MainAxisSize.min, children: [ - Row( - spacing: 12.0, - children: [ - const Icon( - Icons.edit, - size: mediumIconSize, - ), - Flexible( - child: Text( - L10n.of(context).editCourseLater, - style: const TextStyle( - fontSize: descFontSize, + if (controller.widget.mode != + SelectedCourseMode.join) ...[ + Row( + spacing: 12.0, + children: [ + const Icon( + Icons.edit, + size: mediumIconSize, + ), + Flexible( + child: Text( + L10n.of(context).editCourseLater, + style: const TextStyle( + fontSize: descFontSize, + ), ), ), - ), - ], - ), - Row( - spacing: 12.0, - children: [ - const Icon( - Icons.shield, - size: mediumIconSize, - ), - Flexible( - child: Text( - L10n.of(context).newCourseAccess, - style: const TextStyle( - fontSize: descFontSize, + ], + ), + Row( + spacing: 12.0, + children: [ + const Icon( + Icons.shield, + size: mediumIconSize, + ), + Flexible( + child: Text( + L10n.of(context).newCourseAccess, + style: const TextStyle( + fontSize: descFontSize, + ), ), ), - ), - ], - ), + ], + ), + ], Padding( padding: const EdgeInsets.only(top: 8.0), child: ElevatedButton( From 2df61c6a730082c38ebbcf1b34b564e50a4bae84 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Wed, 21 Jan 2026 13:56:48 -0500 Subject: [PATCH 18/18] chore: update knock copy (#5318) --- lib/l10n/intl_ar.arb | 7 ++++++- lib/l10n/intl_be.arb | 7 ++++++- lib/l10n/intl_bn.arb | 7 ++++++- lib/l10n/intl_bo.arb | 7 ++++++- lib/l10n/intl_ca.arb | 7 ++++++- lib/l10n/intl_cs.arb | 7 ++++++- lib/l10n/intl_da.arb | 7 ++++++- lib/l10n/intl_de.arb | 7 ++++++- lib/l10n/intl_el.arb | 7 ++++++- lib/l10n/intl_en.arb | 3 ++- lib/l10n/intl_eo.arb | 7 ++++++- lib/l10n/intl_es.arb | 7 ++++++- lib/l10n/intl_et.arb | 7 ++++++- lib/l10n/intl_eu.arb | 7 ++++++- lib/l10n/intl_fa.arb | 7 ++++++- lib/l10n/intl_fi.arb | 7 ++++++- lib/l10n/intl_fil.arb | 7 ++++++- lib/l10n/intl_fr.arb | 7 ++++++- lib/l10n/intl_ga.arb | 7 ++++++- lib/l10n/intl_gl.arb | 7 ++++++- lib/l10n/intl_he.arb | 7 ++++++- lib/l10n/intl_hi.arb | 7 ++++++- lib/l10n/intl_hr.arb | 7 ++++++- lib/l10n/intl_hu.arb | 7 ++++++- lib/l10n/intl_ia.arb | 7 ++++++- lib/l10n/intl_id.arb | 7 ++++++- lib/l10n/intl_ie.arb | 7 ++++++- lib/l10n/intl_it.arb | 7 ++++++- lib/l10n/intl_ja.arb | 7 ++++++- lib/l10n/intl_ka.arb | 7 ++++++- lib/l10n/intl_ko.arb | 7 ++++++- lib/l10n/intl_lt.arb | 7 ++++++- lib/l10n/intl_lv.arb | 7 ++++++- lib/l10n/intl_nb.arb | 7 ++++++- lib/l10n/intl_nl.arb | 7 ++++++- lib/l10n/intl_pl.arb | 7 ++++++- lib/l10n/intl_pt.arb | 7 ++++++- lib/l10n/intl_pt_BR.arb | 7 ++++++- lib/l10n/intl_pt_PT.arb | 7 ++++++- lib/l10n/intl_ro.arb | 7 ++++++- lib/l10n/intl_ru.arb | 7 ++++++- lib/l10n/intl_sk.arb | 7 ++++++- lib/l10n/intl_sl.arb | 7 ++++++- lib/l10n/intl_sr.arb | 7 ++++++- lib/l10n/intl_sv.arb | 7 ++++++- lib/l10n/intl_ta.arb | 7 ++++++- lib/l10n/intl_te.arb | 7 ++++++- lib/l10n/intl_th.arb | 7 ++++++- lib/l10n/intl_tr.arb | 7 ++++++- lib/l10n/intl_uk.arb | 7 ++++++- lib/l10n/intl_vi.arb | 7 ++++++- lib/l10n/intl_yue.arb | 7 ++++++- lib/l10n/intl_zh.arb | 7 ++++++- lib/l10n/intl_zh_Hant.arb | 7 ++++++- lib/pangea/course_creation/selected_course_page.dart | 2 +- 55 files changed, 321 insertions(+), 55 deletions(-) diff --git a/lib/l10n/intl_ar.arb b/lib/l10n/intl_ar.arb index 3642fb278..8e38e6faf 100644 --- a/lib/l10n/intl_ar.arb +++ b/lib/l10n/intl_ar.arb @@ -1,6 +1,6 @@ { "@@locale": "ar", - "@@last_modified": "2026-01-21 10:42:52.513008", + "@@last_modified": "2026-01-21 13:54:18.388293", "about": "حول", "@about": { "type": "String", @@ -11112,5 +11112,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "تم إرسال طلبك إلى إدارة الدورة! سيتم السماح لك بالدخول إذا وافقوا.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_be.arb b/lib/l10n/intl_be.arb index f7593b805..5cfc00f97 100644 --- a/lib/l10n/intl_be.arb +++ b/lib/l10n/intl_be.arb @@ -1911,7 +1911,7 @@ "playWithAI": "Пакуль гуляйце з ШІ", "courseStartDesc": "Pangea Bot гатовы да працы ў любы час!\n\n...але навучанне лепш з сябрамі!", "@@locale": "be", - "@@last_modified": "2026-01-21 10:42:45.087111", + "@@last_modified": "2026-01-21 13:54:07.402936", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11994,5 +11994,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Ваш запыт быў адпраўлены адміністрацыі курса! Вы будзеце дапушчаны, калі яны зацвердзяць.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_bn.arb b/lib/l10n/intl_bn.arb index 451b639ab..d369b9e4a 100644 --- a/lib/l10n/intl_bn.arb +++ b/lib/l10n/intl_bn.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:43:01.204800", + "@@last_modified": "2026-01-21 13:54:31.328626", "about": "সম্পর্কে", "@about": { "type": "String", @@ -11999,5 +11999,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "আপনার অনুরোধ কোর্স প্রশাসকের কাছে পাঠানো হয়েছে! তারা অনুমোদন করলে আপনাকে প্রবেশ করতে দেওয়া হবে।", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_bo.arb b/lib/l10n/intl_bo.arb index a87317500..39ccde987 100644 --- a/lib/l10n/intl_bo.arb +++ b/lib/l10n/intl_bo.arb @@ -4279,7 +4279,7 @@ "joinPublicTrip": "མི་ཚེས་ལ་ལོག་འབད།", "startOwnTrip": "ངེད་རང་གི་ལོག་ལ་སྦྱོར་བཅོས།", "@@locale": "bo", - "@@last_modified": "2026-01-21 10:42:59.058441", + "@@last_modified": "2026-01-21 13:54:28.767499", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -10649,5 +10649,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Yor requst has been sent to course admin! Yu'll be let in if dey approve.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ca.arb b/lib/l10n/intl_ca.arb index 7c6d2d7fa..3c477421a 100644 --- a/lib/l10n/intl_ca.arb +++ b/lib/l10n/intl_ca.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:46.152113", + "@@last_modified": "2026-01-21 13:54:08.647836", "about": "Quant a", "@about": { "type": "String", @@ -10919,5 +10919,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "La teva sol·licitud s'ha enviat a l'administrador del curs! Et deixaran entrar si ho aproven.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_cs.arb b/lib/l10n/intl_cs.arb index fef398790..8c7cbe62d 100644 --- a/lib/l10n/intl_cs.arb +++ b/lib/l10n/intl_cs.arb @@ -1,6 +1,6 @@ { "@@locale": "cs", - "@@last_modified": "2026-01-21 10:42:42.984636", + "@@last_modified": "2026-01-21 13:54:04.800051", "about": "O aplikaci", "@about": { "type": "String", @@ -11502,5 +11502,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Vaše žádost byla odeslána administrátorovi kurzu! Budete vpuštěni, pokud ji schválí.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_da.arb b/lib/l10n/intl_da.arb index 46dbe3d2c..84a867b38 100644 --- a/lib/l10n/intl_da.arb +++ b/lib/l10n/intl_da.arb @@ -1930,7 +1930,7 @@ "playWithAI": "Leg med AI for nu", "courseStartDesc": "Pangea Bot er klar til at starte når som helst!\n\n...men læring er bedre med venner!", "@@locale": "da", - "@@last_modified": "2026-01-21 10:42:22.819549", + "@@last_modified": "2026-01-21 13:53:34.885532", "@aboutHomeserver": { "type": "String", "placeholders": { @@ -11956,5 +11956,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Din anmodning er sendt til kursusadministratoren! Du vil blive lukket ind, hvis de godkender.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 2f9214e0b..d0cfb6194 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,6 @@ { "@@locale": "de", - "@@last_modified": "2026-01-21 10:42:37.466783", + "@@last_modified": "2026-01-21 13:53:56.595777", "alwaysUse24HourFormat": "true", "@alwaysUse24HourFormat": { "description": "Set to true to always display time of day in 24 hour format." @@ -10902,5 +10902,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Ihre Anfrage wurde an den Kursadministrator gesendet! Sie werden eingelassen, wenn sie zustimmen.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_el.arb b/lib/l10n/intl_el.arb index b90f11cdb..333511f61 100644 --- a/lib/l10n/intl_el.arb +++ b/lib/l10n/intl_el.arb @@ -4456,7 +4456,7 @@ "playWithAI": "Παίξτε με την Τεχνητή Νοημοσύνη προς το παρόν", "courseStartDesc": "Ο Pangea Bot είναι έτοιμος να ξεκινήσει οποιαδήποτε στιγμή!\n\n...αλλά η μάθηση είναι καλύτερη με φίλους!", "@@locale": "el", - "@@last_modified": "2026-01-21 10:43:05.204211", + "@@last_modified": "2026-01-21 13:54:37.783088", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11953,5 +11953,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Το αίτημά σας έχει σταλεί στον διαχειριστή του μαθήματος! Θα σας επιτρέψουν να μπείτε αν το εγκρίνουν.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index e90dfca6b..ae9b71bed 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -5057,5 +5057,6 @@ "fillInBlank": "Fill in the blank with the correct choice", "learn": "Learn", "languageUpdated": "Target language updated!", - "voiceDropdownTitle": "Pangea Bot voice" + "voiceDropdownTitle": "Pangea Bot voice", + "knockDesc": "Your request has been sent to course admin! You'll be let in if they approve." } diff --git a/lib/l10n/intl_eo.arb b/lib/l10n/intl_eo.arb index 942654f9f..4d5067671 100644 --- a/lib/l10n/intl_eo.arb +++ b/lib/l10n/intl_eo.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:43:08.060333", + "@@last_modified": "2026-01-21 13:54:42.193114", "about": "Prio", "@about": { "type": "String", @@ -11984,5 +11984,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Via peto estis sendita al la kursa administranto! Vi estos enirita se ili aprobas.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 4e77d447b..a7ccb8c3c 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,6 @@ { "@@locale": "es", - "@@last_modified": "2026-01-21 10:42:19.343875", + "@@last_modified": "2026-01-21 13:53:29.857658", "about": "Acerca de", "@about": { "type": "String", @@ -8129,5 +8129,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "¡Tu solicitud ha sido enviada al administrador del curso! Te dejarán entrar si la aprueban.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_et.arb b/lib/l10n/intl_et.arb index 30c19ef49..d50c106b2 100644 --- a/lib/l10n/intl_et.arb +++ b/lib/l10n/intl_et.arb @@ -1,6 +1,6 @@ { "@@locale": "et", - "@@last_modified": "2026-01-21 10:42:36.742735", + "@@last_modified": "2026-01-21 13:53:55.586614", "about": "Rakenduse teave", "@about": { "type": "String", @@ -11166,5 +11166,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Teie taotlus on saadetud kursuse administraatorile! Teid lastakse sisse, kui nad heaks kiidavad.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_eu.arb b/lib/l10n/intl_eu.arb index d913d4db3..b8175726d 100644 --- a/lib/l10n/intl_eu.arb +++ b/lib/l10n/intl_eu.arb @@ -1,6 +1,6 @@ { "@@locale": "eu", - "@@last_modified": "2026-01-21 10:42:34.277635", + "@@last_modified": "2026-01-21 13:53:53.122879", "about": "Honi buruz", "@about": { "type": "String", @@ -10895,5 +10895,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Zure eskaera ikastaroaren administratzaileari bidali zaio! Onartzen badute, sartuko zara.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fa.arb b/lib/l10n/intl_fa.arb index 6e395bb02..eb0feb5bd 100644 --- a/lib/l10n/intl_fa.arb +++ b/lib/l10n/intl_fa.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:43:02.232868", + "@@last_modified": "2026-01-21 13:54:33.096668", "repeatPassword": "تکرار رمزعبور", "@repeatPassword": {}, "about": "درباره", @@ -11627,5 +11627,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "درخواست شما به مدیر دوره ارسال شده است! اگر آنها تأیید کنند، شما وارد خواهید شد.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fi.arb b/lib/l10n/intl_fi.arb index 707872200..8684fcf0d 100644 --- a/lib/l10n/intl_fi.arb +++ b/lib/l10n/intl_fi.arb @@ -4009,7 +4009,7 @@ "playWithAI": "Leiki tekoälyn kanssa nyt", "courseStartDesc": "Pangea Bot on valmis milloin tahansa!\n\n...mutta oppiminen on parempaa ystävien kanssa!", "@@locale": "fi", - "@@last_modified": "2026-01-21 10:42:21.857816", + "@@last_modified": "2026-01-21 13:53:33.564588", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11518,5 +11518,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Pyyntösi on lähetetty kurssin ylläpitäjälle! Sinut päästetään sisään, jos he hyväksyvät sen.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fil.arb b/lib/l10n/intl_fil.arb index a51993f5e..2489ec5b6 100644 --- a/lib/l10n/intl_fil.arb +++ b/lib/l10n/intl_fil.arb @@ -2787,7 +2787,7 @@ "selectAll": "Piliin lahat", "deselectAll": "Huwag piliin lahat", "@@locale": "fil", - "@@last_modified": "2026-01-21 10:42:50.349708", + "@@last_modified": "2026-01-21 13:54:14.817261", "@setCustomPermissionLevel": { "type": "String", "placeholders": {} @@ -11871,5 +11871,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Ang iyong kahilingan ay naipadala sa admin ng kurso! Papayagan ka nilang pumasok kung sila ay mag-aapruba.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 58d082c74..700921c07 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,6 +1,6 @@ { "@@locale": "fr", - "@@last_modified": "2026-01-21 10:43:12.775361", + "@@last_modified": "2026-01-21 13:54:48.318824", "about": "À propos", "@about": { "type": "String", @@ -11219,5 +11219,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Votre demande a été envoyée à l'administrateur du cours ! Vous serez admis s'ils approuvent.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ga.arb b/lib/l10n/intl_ga.arb index b6e3263eb..9abadb3a0 100644 --- a/lib/l10n/intl_ga.arb +++ b/lib/l10n/intl_ga.arb @@ -4517,7 +4517,7 @@ "playWithAI": "Imir le AI faoi láthair", "courseStartDesc": "Tá Bot Pangea réidh chun dul am ar bith!\n\n...ach is fearr foghlaim le cairde!", "@@locale": "ga", - "@@last_modified": "2026-01-21 10:43:11.760663", + "@@last_modified": "2026-01-21 13:54:46.978792", "@customReaction": { "type": "String", "placeholders": {} @@ -10893,5 +10893,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Tá do hiarratas curtha chuig an riarachán cúrsa! Cuirfear isteach thú má cheadaíonn siad é.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_gl.arb b/lib/l10n/intl_gl.arb index 28655e165..23f338731 100644 --- a/lib/l10n/intl_gl.arb +++ b/lib/l10n/intl_gl.arb @@ -1,6 +1,6 @@ { "@@locale": "gl", - "@@last_modified": "2026-01-21 10:42:20.142432", + "@@last_modified": "2026-01-21 13:53:31.619299", "about": "Acerca de", "@about": { "type": "String", @@ -10892,5 +10892,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "A túa solicitude foi enviada ao administrador do curso! Serás admitido se a aproban.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_he.arb b/lib/l10n/intl_he.arb index f36caa914..af9e0ad59 100644 --- a/lib/l10n/intl_he.arb +++ b/lib/l10n/intl_he.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:30.598205", + "@@last_modified": "2026-01-21 13:53:47.592162", "about": "אודות", "@about": { "type": "String", @@ -11944,5 +11944,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "הבקשה שלך נשלחה למנהל הקורס! תורשה להיכנס אם הם יאשרו.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hi.arb b/lib/l10n/intl_hi.arb index 611db19dd..064cbe283 100644 --- a/lib/l10n/intl_hi.arb +++ b/lib/l10n/intl_hi.arb @@ -4483,7 +4483,7 @@ "playWithAI": "अभी के लिए एआई के साथ खेलें", "courseStartDesc": "पैंजिया बॉट कभी भी जाने के लिए तैयार है!\n\n...लेकिन दोस्तों के साथ सीखना बेहतर है!", "@@locale": "hi", - "@@last_modified": "2026-01-21 10:43:07.054066", + "@@last_modified": "2026-01-21 13:54:40.850433", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11980,5 +11980,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "आपका अनुरोध पाठ्यक्रम प्रशासन को भेज दिया गया है! यदि वे स्वीकृत करते हैं, तो आपको अंदर जाने दिया जाएगा।", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hr.arb b/lib/l10n/intl_hr.arb index 17ea4d22e..6b8517e96 100644 --- a/lib/l10n/intl_hr.arb +++ b/lib/l10n/intl_hr.arb @@ -1,6 +1,6 @@ { "@@locale": "hr", - "@@last_modified": "2026-01-21 10:42:29.581714", + "@@last_modified": "2026-01-21 13:53:46.524141", "about": "Informacije", "@about": { "type": "String", @@ -11267,5 +11267,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Vaš zahtjev je poslan administratoru tečaja! Bit ćete primljeni ako odobre.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_hu.arb b/lib/l10n/intl_hu.arb index 490d3b22f..b0767225a 100644 --- a/lib/l10n/intl_hu.arb +++ b/lib/l10n/intl_hu.arb @@ -1,6 +1,6 @@ { "@@locale": "hu", - "@@last_modified": "2026-01-21 10:42:24.298395", + "@@last_modified": "2026-01-21 13:53:36.742109", "about": "Névjegy", "@about": { "type": "String", @@ -10896,5 +10896,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "A kérésed el lett küldve a kurzus adminisztrátorának! Be fogsz engedni, ha jóváhagyják.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ia.arb b/lib/l10n/intl_ia.arb index 54974eb51..506658635 100644 --- a/lib/l10n/intl_ia.arb +++ b/lib/l10n/intl_ia.arb @@ -1958,7 +1958,7 @@ "playWithAI": "Joca con le IA pro ora", "courseStartDesc": "Pangea Bot es preste a comenzar a qualunque momento!\n\n...ma apprender es melior con amicos!", "@@locale": "ia", - "@@last_modified": "2026-01-21 10:42:31.403920", + "@@last_modified": "2026-01-21 13:53:49.028067", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11973,5 +11973,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Tua peticio est missa ad administratorem cursuum! Te admittent si illi approbant.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_id.arb b/lib/l10n/intl_id.arb index 2d02858ce..5db3bb912 100644 --- a/lib/l10n/intl_id.arb +++ b/lib/l10n/intl_id.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:25.871973", + "@@last_modified": "2026-01-21 13:53:37.899898", "setAsCanonicalAlias": "Atur sebagai alias utama", "@setAsCanonicalAlias": { "type": "String", @@ -10886,5 +10886,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Permintaan Anda telah dikirim ke admin kursus! Anda akan diizinkan masuk jika mereka menyetujuinya.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ie.arb b/lib/l10n/intl_ie.arb index caf59b6b4..3d38e5615 100644 --- a/lib/l10n/intl_ie.arb +++ b/lib/l10n/intl_ie.arb @@ -4372,7 +4372,7 @@ "playWithAI": "Joca con AI pro ora", "courseStartDesc": "Pangea Bot es preste a partir a qualunque momento!\n\n...ma apprender es melior con amicos!", "@@locale": "ie", - "@@last_modified": "2026-01-21 10:42:28.431424", + "@@last_modified": "2026-01-21 13:53:45.416677", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11869,5 +11869,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Tua iarrtas a chaidh a chur gu rianachd a' chùrsa! Thèid thu a leigeil a-steach ma tha iad a' freagairt gu math.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 9284e09b3..312c08f22 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:40.690823", + "@@last_modified": "2026-01-21 13:54:01.568848", "about": "Informazioni", "@about": { "type": "String", @@ -10898,5 +10898,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "La tua richiesta è stata inviata all'amministratore del corso! Sarai ammesso se approvano.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ja.arb b/lib/l10n/intl_ja.arb index c83be27b4..c511c0107 100644 --- a/lib/l10n/intl_ja.arb +++ b/lib/l10n/intl_ja.arb @@ -1,6 +1,6 @@ { "@@locale": "ja", - "@@last_modified": "2026-01-21 10:43:06.128364", + "@@last_modified": "2026-01-21 13:54:39.359536", "about": "このアプリについて", "@about": { "type": "String", @@ -11685,5 +11685,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "あなたのリクエストはコース管理者に送信されました! 彼らが承認すれば、入ることができます。", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ka.arb b/lib/l10n/intl_ka.arb index d8e600706..7c3ce8707 100644 --- a/lib/l10n/intl_ka.arb +++ b/lib/l10n/intl_ka.arb @@ -2594,7 +2594,7 @@ "playWithAI": "ამ დროისთვის ითამაშეთ AI-თან", "courseStartDesc": "Pangea Bot მზადაა ნებისმიერ დროს გასასვლელად!\n\n...მაგრამ სწავლა უკეთესია მეგობრებთან ერთად!", "@@locale": "ka", - "@@last_modified": "2026-01-21 10:43:09.701292", + "@@last_modified": "2026-01-21 13:54:44.486907", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11925,5 +11925,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "თქვენი მოთხოვნა გაგზავნილია კურსის ადმინისტრატორთან! თქვენ შეგიშვებენ, თუ ისინი დაამტკიცებენ.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ko.arb b/lib/l10n/intl_ko.arb index ed5289391..655637228 100644 --- a/lib/l10n/intl_ko.arb +++ b/lib/l10n/intl_ko.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:17.603097", + "@@last_modified": "2026-01-21 13:53:28.417406", "about": "소개", "@about": { "type": "String", @@ -11003,5 +11003,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "귀하의 요청이 과정 관리자에게 전송되었습니다! 그들이 승인하면 들어갈 수 있습니다.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_lt.arb b/lib/l10n/intl_lt.arb index 35ca44e54..3473469a9 100644 --- a/lib/l10n/intl_lt.arb +++ b/lib/l10n/intl_lt.arb @@ -3861,7 +3861,7 @@ "playWithAI": "Žaiskite su dirbtiniu intelektu dabar", "courseStartDesc": "Pangea botas pasiruošęs bet kada pradėti!\n\n...bet mokymasis yra geresnis su draugais!", "@@locale": "lt", - "@@last_modified": "2026-01-21 10:42:55.447627", + "@@last_modified": "2026-01-21 13:54:22.729769", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11700,5 +11700,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Jūsų prašymas buvo išsiųstas kurso administratoriui! Būsite įleistas, jei jie patvirtins.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_lv.arb b/lib/l10n/intl_lv.arb index 64bbaf040..feff58b67 100644 --- a/lib/l10n/intl_lv.arb +++ b/lib/l10n/intl_lv.arb @@ -4482,7 +4482,7 @@ "playWithAI": "Tagad spēlējiet ar AI", "courseStartDesc": "Pangea bots ir gatavs jebkurā laikā!\n\n...bet mācīties ir labāk ar draugiem!", "@@locale": "lv", - "@@last_modified": "2026-01-21 10:42:51.147364", + "@@last_modified": "2026-01-21 13:54:16.883169", "analyticsInactiveTitle": "Pieprasījumi neaktīviem lietotājiem nevar tikt nosūtīti", "analyticsInactiveDesc": "Neaktīvi lietotāji, kuri nav pieteikušies kopš šīs funkcijas ieviešanas, neredzēs jūsu pieprasījumu.\n\nPieprasījuma poga parādīsies, kad viņi atgriezīsies. Jūs varat atkārtoti nosūtīt pieprasījumu vēlāk, noklikšķinot uz pieprasījuma pogas viņu vārdā, kad tā būs pieejama.", "accessRequestedTitle": "Pieprasījums piekļūt analītikai", @@ -10881,5 +10881,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Jūsu pieprasījums ir nosūtīts kursa administratoram! Jūs tiksiet iekšā, ja viņi apstiprinās.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_nb.arb b/lib/l10n/intl_nb.arb index 5615f0550..f87cddbc8 100644 --- a/lib/l10n/intl_nb.arb +++ b/lib/l10n/intl_nb.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:43.891386", + "@@last_modified": "2026-01-21 13:54:06.232198", "about": "Om", "@about": { "type": "String", @@ -11988,5 +11988,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Din forespørsel har blitt sendt til kursadministratoren! Du vil bli sluppet inn hvis de godkjenner.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_nl.arb b/lib/l10n/intl_nl.arb index b021f9ad1..ed586d553 100644 --- a/lib/l10n/intl_nl.arb +++ b/lib/l10n/intl_nl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:58.326124", + "@@last_modified": "2026-01-21 13:54:27.649909", "about": "Over ons", "@about": { "type": "String", @@ -10895,5 +10895,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Je verzoek is verzonden naar de cursusbeheerder! Je wordt toegelaten als ze goedkeuren.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 9e3d32b91..d7d2c73a5 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,6 @@ { "@@locale": "pl", - "@@last_modified": "2026-01-21 10:43:03.257336", + "@@last_modified": "2026-01-21 13:54:34.451816", "about": "O aplikacji", "@about": { "type": "String", @@ -10893,5 +10893,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Twoja prośba została wysłana do administratora kursu! Zostaniesz wpuszczony, jeśli ją zatwierdzą.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index f80792528..d9b54e65a 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:35.392050", + "@@last_modified": "2026-01-21 13:53:54.148542", "copiedToClipboard": "Copiada para a área de transferência", "@copiedToClipboard": { "type": "String", @@ -11995,5 +11995,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Sua solicitação foi enviada ao administrador do curso! Você será admitido se eles aprovarem.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt_BR.arb b/lib/l10n/intl_pt_BR.arb index 933c9f8f3..11797eb83 100644 --- a/lib/l10n/intl_pt_BR.arb +++ b/lib/l10n/intl_pt_BR.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:32.545549", + "@@last_modified": "2026-01-21 13:53:51.587209", "about": "Sobre", "@about": { "type": "String", @@ -11253,5 +11253,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Sua solicitação foi enviada ao administrador do curso! Você será admitido se eles aprovarem.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_pt_PT.arb b/lib/l10n/intl_pt_PT.arb index 643c67fee..bc97480dc 100644 --- a/lib/l10n/intl_pt_PT.arb +++ b/lib/l10n/intl_pt_PT.arb @@ -3331,7 +3331,7 @@ "selectAll": "Selecionar tudo", "deselectAll": "Desmarcar tudo", "@@locale": "pt_PT", - "@@last_modified": "2026-01-21 10:42:48.198355", + "@@last_modified": "2026-01-21 13:54:11.801274", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11924,5 +11924,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Sua solicitação foi enviada ao administrador do curso! Você será admitido se eles aprovarem.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ro.arb b/lib/l10n/intl_ro.arb index 7e868c193..3f458ad96 100644 --- a/lib/l10n/intl_ro.arb +++ b/lib/l10n/intl_ro.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:26.609980", + "@@last_modified": "2026-01-21 13:53:41.456181", "about": "Despre", "@about": { "type": "String", @@ -11630,5 +11630,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Cererea ta a fost trimisă administratorului cursului! Vei fi lăsat să intri dacă ei aprobă.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index e08d12d6e..976018aac 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -1,6 +1,6 @@ { "@@locale": "ru", - "@@last_modified": "2026-01-21 10:43:08.936913", + "@@last_modified": "2026-01-21 13:54:43.229000", "about": "О проекте", "@about": { "type": "String", @@ -11000,5 +11000,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Ваш запрос отправлен администратору курса! Вы будете допущены, если они одобрят.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sk.arb b/lib/l10n/intl_sk.arb index 26dd291f9..510156730 100644 --- a/lib/l10n/intl_sk.arb +++ b/lib/l10n/intl_sk.arb @@ -1,6 +1,6 @@ { "@@locale": "sk", - "@@last_modified": "2026-01-21 10:42:27.675920", + "@@last_modified": "2026-01-21 13:53:43.706457", "about": "O aplikácii", "@about": { "type": "String", @@ -11979,5 +11979,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Vaša žiadosť bola odoslaná administrátorovi kurzu! Budete vpustený, ak ju schvália.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sl.arb b/lib/l10n/intl_sl.arb index a9889e3a1..9736fb5f5 100644 --- a/lib/l10n/intl_sl.arb +++ b/lib/l10n/intl_sl.arb @@ -2464,7 +2464,7 @@ "playWithAI": "Za zdaj igrajte z AI-jem", "courseStartDesc": "Pangea Bot je pripravljen kadarkoli!\n\n...ampak je bolje učiti se s prijatelji!", "@@locale": "sl", - "@@last_modified": "2026-01-21 10:42:38.801732", + "@@last_modified": "2026-01-21 13:53:58.241980", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11976,5 +11976,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Vaša zahteva je bila poslana skrbniku tečaja! Vstopili boste, če jo odobrijo.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sr.arb b/lib/l10n/intl_sr.arb index c32c01635..0b970dceb 100644 --- a/lib/l10n/intl_sr.arb +++ b/lib/l10n/intl_sr.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:43:10.733270", + "@@last_modified": "2026-01-21 13:54:45.561732", "about": "О програму", "@about": { "type": "String", @@ -11997,5 +11997,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Vaš zahtev je poslat administratoru kursa! Bićete primljeni ako odobre.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_sv.arb b/lib/l10n/intl_sv.arb index 9bffc0e88..302e97dde 100644 --- a/lib/l10n/intl_sv.arb +++ b/lib/l10n/intl_sv.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:43:04.367889", + "@@last_modified": "2026-01-21 13:54:35.931933", "about": "Om", "@about": { "type": "String", @@ -11373,5 +11373,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Din begäran har skickats till kursadministratören! Du kommer att släppas in om de godkänner.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_ta.arb b/lib/l10n/intl_ta.arb index 5592467b6..82050d0e3 100644 --- a/lib/l10n/intl_ta.arb +++ b/lib/l10n/intl_ta.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:57.399546", + "@@last_modified": "2026-01-21 13:54:25.999605", "acceptedTheInvitation": "👍 {username} அழைப்பை ஏற்றுக்கொண்டது", "@acceptedTheInvitation": { "type": "String", @@ -11119,5 +11119,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "உங்கள் கோரிக்கை பாடம் நிர்வாகிக்கு அனுப்பப்பட்டுள்ளது! அவர்கள் ஒப்புதலளித்தால் நீங்கள் உள்ளே அனுமதிக்கப்படுவீர்கள்.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_te.arb b/lib/l10n/intl_te.arb index 9e1e5fe94..e457db572 100644 --- a/lib/l10n/intl_te.arb +++ b/lib/l10n/intl_te.arb @@ -1920,7 +1920,7 @@ "playWithAI": "ఇప్పుడే AI తో ఆడండి", "courseStartDesc": "పాంజియా బాట్ ఎప్పుడైనా సిద్ధంగా ఉంటుంది!\n\n...కానీ స్నేహితులతో నేర్చుకోవడం మెరుగైనది!", "@@locale": "te", - "@@last_modified": "2026-01-21 10:42:54.652537", + "@@last_modified": "2026-01-21 13:54:20.897642", "@setCustomPermissionLevel": { "type": "String", "placeholders": {} @@ -11984,5 +11984,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "మీ అభ్యర్థన కోర్సు నిర్వాహకుడికి పంపబడింది! వారు ఆమోదిస్తే, మీరు లోపలికి రానున్నారు.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_th.arb b/lib/l10n/intl_th.arb index b07d69284..397aaa446 100644 --- a/lib/l10n/intl_th.arb +++ b/lib/l10n/intl_th.arb @@ -4456,7 +4456,7 @@ "playWithAI": "เล่นกับ AI ชั่วคราว", "courseStartDesc": "Pangea Bot พร้อมที่จะเริ่มต้นได้ทุกเมื่อ!\n\n...แต่การเรียนรู้ดีกว่ากับเพื่อน!", "@@locale": "th", - "@@last_modified": "2026-01-21 10:42:47.175182", + "@@last_modified": "2026-01-21 13:54:10.758435", "@alwaysUse24HourFormat": { "type": "String", "placeholders": {} @@ -11953,5 +11953,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "คำขอของคุณได้ถูกส่งไปยังผู้ดูแลหลักสูตรแล้ว! คุณจะได้รับอนุญาตให้เข้าหากพวกเขาอนุมัติ.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_tr.arb b/lib/l10n/intl_tr.arb index fe247f360..f38520405 100644 --- a/lib/l10n/intl_tr.arb +++ b/lib/l10n/intl_tr.arb @@ -1,6 +1,6 @@ { "@@locale": "tr", - "@@last_modified": "2026-01-21 10:42:53.424381", + "@@last_modified": "2026-01-21 13:54:19.467039", "about": "Hakkında", "@about": { "type": "String", @@ -11117,5 +11117,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Talebiniz kurs yöneticisine gönderildi! Onaylarlarsa içeri alınacaksınız.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_uk.arb b/lib/l10n/intl_uk.arb index 7734ef4fe..5dbfc2938 100644 --- a/lib/l10n/intl_uk.arb +++ b/lib/l10n/intl_uk.arb @@ -1,6 +1,6 @@ { "@@locale": "uk", - "@@last_modified": "2026-01-21 10:42:41.558170", + "@@last_modified": "2026-01-21 13:54:03.403456", "about": "Про застосунок", "@about": { "type": "String", @@ -10889,5 +10889,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Ваш запит надіслано адміністратору курсу! Ви будете допущені, якщо вони схвалять.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_vi.arb b/lib/l10n/intl_vi.arb index 0d9c741ab..89760d972 100644 --- a/lib/l10n/intl_vi.arb +++ b/lib/l10n/intl_vi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:56.498587", + "@@last_modified": "2026-01-21 13:54:24.252269", "about": "Giới thiệu", "@about": { "type": "String", @@ -6465,5 +6465,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "Yêu cầu của bạn đã được gửi đến quản trị viên khóa học! Bạn sẽ được cho vào nếu họ chấp thuận.", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_yue.arb b/lib/l10n/intl_yue.arb index 15558c19a..b280b4a62 100644 --- a/lib/l10n/intl_yue.arb +++ b/lib/l10n/intl_yue.arb @@ -1856,7 +1856,7 @@ "selectAll": "全選", "deselectAll": "取消全選", "@@locale": "yue", - "@@last_modified": "2026-01-21 10:42:39.595731", + "@@last_modified": "2026-01-21 13:53:59.885092", "@ignoreUser": { "type": "String", "placeholders": {} @@ -11986,5 +11986,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "你的請求已經發送給課程管理員!如果他們批准,你將被允許進入。", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index e3b4d480b..ffe826b05 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -1,6 +1,6 @@ { "@@locale": "zh", - "@@last_modified": "2026-01-21 10:43:00.180549", + "@@last_modified": "2026-01-21 13:54:29.681537", "about": "关于", "@about": { "type": "String", @@ -10886,5 +10886,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "您的请求已发送给课程管理员!如果他们批准,您将被允许进入。", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_zh_Hant.arb b/lib/l10n/intl_zh_Hant.arb index 3491a8f78..fc92bb5dc 100644 --- a/lib/l10n/intl_zh_Hant.arb +++ b/lib/l10n/intl_zh_Hant.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-01-21 10:42:49.437032", + "@@last_modified": "2026-01-21 13:54:13.165623", "about": "關於", "@about": { "type": "String", @@ -10893,5 +10893,10 @@ "@voiceDropdownTitle": { "type": "String", "placeholders": {} + }, + "knockDesc": "您的請求已發送給課程管理員!如果他們批准,您將被允許進入。", + "@knockDesc": { + "type": "String", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/pangea/course_creation/selected_course_page.dart b/lib/pangea/course_creation/selected_course_page.dart index 299c784f4..34f913b08 100644 --- a/lib/pangea/course_creation/selected_course_page.dart +++ b/lib/pangea/course_creation/selected_course_page.dart @@ -179,7 +179,7 @@ class SelectedCourseController extends State await showOkAlertDialog( context: context, title: L10n.of(context).youHaveKnocked, - message: L10n.of(context).pleaseWaitUntilInvited, + message: L10n.of(context).knockDesc, ); return; }