From a56ba59316db0cf2e6a5a9033ecebc8a839933f1 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 3 Dec 2025 12:07:36 -0500 Subject: [PATCH 1/9] initial work to remove speech to text controller from pangea controller --- .../common/controllers/pangea_controller.dart | 2 +- .../event_wrappers/pangea_message_event.dart | 18 +- .../models/representation_content_model.dart | 6 +- .../audio_encoding_enum.dart | 5 +- .../speech_to_text/speech_to_text_repo.dart | 105 +++++++ .../speech_to_text_request_model.dart | 70 +++++ .../speech_to_text_response_model.dart} | 274 ++++++------------ .../speech_to_text_controller.dart | 133 --------- .../widgets/select_mode_controller.dart | 8 +- .../widgets/stt_transcript_tokens.dart | 4 +- 10 files changed, 291 insertions(+), 334 deletions(-) rename lib/pangea/{toolbar/enums => speech_to_text}/audio_encoding_enum.dart (89%) create mode 100644 lib/pangea/speech_to_text/speech_to_text_repo.dart create mode 100644 lib/pangea/speech_to_text/speech_to_text_request_model.dart rename lib/pangea/{toolbar/models/speech_to_text_models.dart => speech_to_text/speech_to_text_response_model.dart} (65%) delete mode 100644 lib/pangea/toolbar/controllers/speech_to_text_controller.dart diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index 7d8d7ef33..f61209428 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -21,8 +21,8 @@ import 'package:fluffychat/pangea/learning_settings/controllers/language_control import 'package:fluffychat/pangea/learning_settings/utils/locale_provider.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; import 'package:fluffychat/pangea/spaces/controllers/space_code_controller.dart'; +import 'package:fluffychat/pangea/speech_to_text/speech_to_text_controller.dart'; import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart'; -import 'package:fluffychat/pangea/toolbar/controllers/speech_to_text_controller.dart'; import 'package:fluffychat/pangea/toolbar/controllers/text_to_speech_controller.dart'; import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart'; import 'package:fluffychat/pangea/user/controllers/permissions_controller.dart'; diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index ef75245e4..be1643194 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -19,9 +19,10 @@ import 'package:fluffychat/pangea/events/repo/language_detection_request.dart'; import 'package:fluffychat/pangea/events/repo/language_detection_response.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; import 'package:fluffychat/pangea/spaces/models/space_model.dart'; +import 'package:fluffychat/pangea/speech_to_text/audio_encoding_enum.dart'; +import 'package:fluffychat/pangea/speech_to_text/speech_to_text_request_model.dart'; +import 'package:fluffychat/pangea/speech_to_text/speech_to_text_response_model.dart'; import 'package:fluffychat/pangea/toolbar/controllers/text_to_speech_controller.dart'; -import 'package:fluffychat/pangea/toolbar/enums/audio_encoding_enum.dart'; -import 'package:fluffychat/pangea/toolbar/models/speech_to_text_models.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_audio_card.dart'; import 'package:fluffychat/pangea/translation/full_text_translation_request_model.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; @@ -227,13 +228,13 @@ class PangeaMessageEvent { null; }).toSet(); - SpeechToTextModel? getSpeechToTextLocal() { + SpeechToTextResponseModel? getSpeechToTextLocal() { final rawBotTranscription = event.content.tryGetMap(ModelKey.botTranscription); if (rawBotTranscription != null) { try { - return SpeechToTextModel.fromJson( + return SpeechToTextResponseModel.fromJson( Map.from(rawBotTranscription), ); } catch (err, s) { @@ -257,7 +258,7 @@ class PangeaMessageEvent { .speechToText; } - Future getSpeechToText( + Future getSpeechToText( String l1Code, String l2Code, ) async { @@ -268,7 +269,8 @@ class PangeaMessageEvent { final rawBotTranscription = event.content.tryGetMap(ModelKey.botTranscription); if (rawBotTranscription != null) { - final SpeechToTextModel botTranscription = SpeechToTextModel.fromJson( + final SpeechToTextResponseModel botTranscription = + SpeechToTextResponseModel.fromJson( Map.from(rawBotTranscription), ); @@ -290,7 +292,7 @@ class PangeaMessageEvent { return botTranscription; } - final SpeechToTextModel? speechToTextLocal = representations + final SpeechToTextResponseModel? speechToTextLocal = representations .firstWhereOrNull( (element) => element.content.speechToText != null, ) @@ -303,7 +305,7 @@ class PangeaMessageEvent { final matrixFile = await _event.downloadAndDecryptAttachment(); - final SpeechToTextModel response = + final SpeechToTextResponseModel response = await MatrixState.pangeaController.speechToText.get( SpeechToTextRequestModel( audioContent: matrixFile.bytes, diff --git a/lib/pangea/events/models/representation_content_model.dart b/lib/pangea/events/models/representation_content_model.dart index 36c41cd3e..9bf6b0c97 100644 --- a/lib/pangea/events/models/representation_content_model.dart +++ b/lib/pangea/events/models/representation_content_model.dart @@ -8,7 +8,7 @@ import 'package:fluffychat/pangea/choreographer/choreo_record_model.dart'; import 'package:fluffychat/pangea/choreographer/igc/pangea_match_status_enum.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; -import 'package:fluffychat/pangea/toolbar/models/speech_to_text_models.dart'; +import 'package:fluffychat/pangea/speech_to_text/speech_to_text_response_model.dart'; import 'package:fluffychat/widgets/matrix.dart'; /// this class is contained within a [RepresentationEvent] @@ -30,7 +30,7 @@ class PangeaRepresentation { bool originalWritten; // a representation can be create via speech to text on the original message - SpeechToTextModel? speechToText; + SpeechToTextResponseModel? speechToText; // how do we know which representation was sent by author? // RepresentationEvent.text == PangeaMessageEvent.event.body @@ -70,7 +70,7 @@ class PangeaRepresentation { originalWritten: json[_originalWrittenKey] ?? false, speechToText: json[_speechToTextKey] == null ? null - : SpeechToTextModel.fromJson(json[_speechToTextKey]), + : SpeechToTextResponseModel.fromJson(json[_speechToTextKey]), ); } diff --git a/lib/pangea/toolbar/enums/audio_encoding_enum.dart b/lib/pangea/speech_to_text/audio_encoding_enum.dart similarity index 89% rename from lib/pangea/toolbar/enums/audio_encoding_enum.dart rename to lib/pangea/speech_to_text/audio_encoding_enum.dart index 812648327..ee9236ffa 100644 --- a/lib/pangea/toolbar/enums/audio_encoding_enum.dart +++ b/lib/pangea/speech_to_text/audio_encoding_enum.dart @@ -26,11 +26,8 @@ enum AudioEncodingEnum { speexWithHeaderByte, mp3, mp4, - webmOpus, -} + webmOpus; -// Utility extension to map enum values to their corresponding string value as used by the API -extension AudioEncodingExtension on AudioEncodingEnum { String get value { switch (this) { case AudioEncodingEnum.linear16: diff --git a/lib/pangea/speech_to_text/speech_to_text_repo.dart b/lib/pangea/speech_to_text/speech_to_text_repo.dart new file mode 100644 index 000000000..c6802e749 --- /dev/null +++ b/lib/pangea/speech_to_text/speech_to_text_repo.dart @@ -0,0 +1,105 @@ +import 'dart:convert'; + +import 'package:async/async.dart'; +import 'package:http/http.dart'; + +import 'package:fluffychat/pangea/common/config/environment.dart'; +import 'package:fluffychat/pangea/common/network/requests.dart'; +import 'package:fluffychat/pangea/common/network/urls.dart'; +import 'package:fluffychat/pangea/common/utils/error_handler.dart'; +import 'package:fluffychat/pangea/speech_to_text/speech_to_text_request_model.dart'; +import 'package:fluffychat/pangea/speech_to_text/speech_to_text_response_model.dart'; + +class _SpeechToTextCacheItem { + final Future data; + final DateTime timestamp; + + const _SpeechToTextCacheItem({ + required this.data, + required this.timestamp, + }); +} + +class SpeechToTextRepo { + static final Map _cache = {}; + static const Duration _cacheDuration = Duration(minutes: 10); + + static Future> get( + String accessToken, + SpeechToTextRequestModel request, + ) { + final cached = _getCached(request); + if (cached != null) { + return _getResult(request, cached); + } + + final future = _fetch(accessToken, request); + _setCached(request, future); + return _getResult(request, future); + } + + static Future _fetch( + String accessToken, + SpeechToTextRequestModel request, + ) async { + final Requests req = Requests( + choreoApiKey: Environment.choreoApiKey, + accessToken: accessToken, + ); + + final Response res = await req.post( + url: PApiUrls.simpleTranslation, + body: request.toJson(), + ); + + if (res.statusCode != 200) { + throw Exception( + 'Failed to translate text: ${res.statusCode} ${res.reasonPhrase}', + ); + } + + return SpeechToTextResponseModel.fromJson( + jsonDecode(utf8.decode(res.bodyBytes)), + ); + } + + static Future> _getResult( + SpeechToTextRequestModel request, + Future future, + ) async { + try { + final res = await future; + return Result.value(res); + } catch (e, s) { + _cache.remove(request.hashCode.toString()); + ErrorHandler.logError( + e: e, + s: s, + data: request.toJson(), + ); + return Result.error(e); + } + } + + static Future? _getCached( + SpeechToTextRequestModel request, + ) { + final cacheKeys = [..._cache.keys]; + for (final key in cacheKeys) { + if (DateTime.now().difference(_cache[key]!.timestamp) >= _cacheDuration) { + _cache.remove(key); + } + } + + return _cache[request.hashCode.toString()]?.data; + } + + static void _setCached( + SpeechToTextRequestModel request, + Future response, + ) => + _cache[request.hashCode.toString()] = _SpeechToTextCacheItem( + data: response, + timestamp: DateTime.now(), + ); +} diff --git a/lib/pangea/speech_to_text/speech_to_text_request_model.dart b/lib/pangea/speech_to_text/speech_to_text_request_model.dart new file mode 100644 index 000000000..a0a736ff6 --- /dev/null +++ b/lib/pangea/speech_to_text/speech_to_text_request_model.dart @@ -0,0 +1,70 @@ +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; + +import 'package:matrix/matrix.dart'; + +import 'package:fluffychat/pangea/speech_to_text/audio_encoding_enum.dart'; + +class SpeechToTextRequestModel { + final Uint8List audioContent; + final SpeechToTextAudioConfigModel config; + final Event? audioEvent; + + SpeechToTextRequestModel({ + required this.audioContent, + required this.config, + this.audioEvent, + }); + + Map toJson() => { + "audio_content": base64Encode(audioContent), + "config": config.toJson(), + }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other is! SpeechToTextRequestModel) return false; + + return listEquals(audioContent, other.audioContent) && + config == other.config; + } + + @override + int get hashCode { + final bytesSample = + audioContent.length > 10 ? audioContent.sublist(0, 10) : audioContent; + return Object.hashAll([ + Object.hashAll(bytesSample), + config.hashCode, + ]); + } +} + +class SpeechToTextAudioConfigModel { + final AudioEncodingEnum encoding; + final int sampleRateHertz; + final bool enableWordConfidence; + final bool enableAutomaticPunctuation; + final String userL1; + final String userL2; + + SpeechToTextAudioConfigModel({ + required this.encoding, + required this.userL1, + required this.userL2, + this.sampleRateHertz = 16000, + this.enableWordConfidence = true, + this.enableAutomaticPunctuation = true, + }); + + Map toJson() => { + "encoding": encoding.value, + "sample_rate_hertz": sampleRateHertz, + "user_l1": userL1, + "user_l2": userL2, + "enable_word_confidence": enableWordConfidence, + "enable_automatic_punctuation": enableAutomaticPunctuation, + }; +} diff --git a/lib/pangea/toolbar/models/speech_to_text_models.dart b/lib/pangea/speech_to_text/speech_to_text_response_model.dart similarity index 65% rename from lib/pangea/toolbar/models/speech_to_text_models.dart rename to lib/pangea/speech_to_text/speech_to_text_response_model.dart index ffb2a7088..e042be077 100644 --- a/lib/pangea/toolbar/models/speech_to_text_models.dart +++ b/lib/pangea/speech_to_text/speech_to_text_response_model.dart @@ -1,79 +1,118 @@ -import 'dart:convert'; - -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:matrix/matrix.dart'; - import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart'; import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; -import 'package:fluffychat/pangea/toolbar/enums/audio_encoding_enum.dart'; -const int thresholdForGreen = 80; +class SpeechToTextResponseModel { + final List results; -class SpeechToTextAudioConfigModel { - final AudioEncodingEnum encoding; - final int sampleRateHertz; - final bool enableWordConfidence; - final bool enableAutomaticPunctuation; - final String userL1; - final String userL2; - - SpeechToTextAudioConfigModel({ - required this.encoding, - required this.userL1, - required this.userL2, - this.sampleRateHertz = 16000, - this.enableWordConfidence = true, - this.enableAutomaticPunctuation = true, + SpeechToTextResponseModel({ + required this.results, }); + Transcript get transcript => results.first.transcripts.first; + + String get langCode => results.first.transcripts.first.langCode; + + factory SpeechToTextResponseModel.fromJson(Map json) { + final results = json['results'] as List; + if (results.isEmpty) { + throw Exception('SpeechToTextModel.fromJson: results is empty'); + } + return SpeechToTextResponseModel( + results: (json['results'] as List) + .map((e) => SpeechToTextResult.fromJson(e)) + .toList(), + ); + } + Map toJson() => { - "encoding": encoding.value, - "sample_rate_hertz": sampleRateHertz, - "user_l1": userL1, - "user_l2": userL2, - "enable_word_confidence": enableWordConfidence, - "enable_automatic_punctuation": enableAutomaticPunctuation, + "results": results.map((e) => e.toJson()).toList(), + }; + + List constructs( + String roomId, + String eventId, + ) { + final List constructs = []; + final metadata = ConstructUseMetaData( + roomId: roomId, + eventId: eventId, + timeStamp: DateTime.now(), + ); + for (final sstToken in transcript.sttTokens) { + final token = sstToken.token; + if (!token.lemma.saveVocab) continue; + constructs.addAll( + token.allUses( + ConstructUseTypeEnum.pvm, + metadata, + ConstructUseTypeEnum.pvm.pointValue, + ), + ); + } + return constructs; + } +} + +class SpeechToTextResult { + final List transcripts; + + SpeechToTextResult({required this.transcripts}); + + factory SpeechToTextResult.fromJson(Map json) => + SpeechToTextResult( + transcripts: (json['transcripts'] as List) + .map((e) => Transcript.fromJson(e)) + .toList(), + ); + + Map toJson() => { + "transcripts": transcripts.map((e) => e.toJson()).toList(), }; } -class SpeechToTextRequestModel { - final Uint8List audioContent; - final SpeechToTextAudioConfigModel config; - final Event? audioEvent; +class Transcript { + final String text; + final int confidence; + final List sttTokens; + final String langCode; + final int? wordsPerHr; - SpeechToTextRequestModel({ - required this.audioContent, - required this.config, - this.audioEvent, + Transcript({ + required this.text, + required this.confidence, + required this.sttTokens, + required this.langCode, + required this.wordsPerHr, }); + /// Returns the number of words per minute rounded to one decimal place. + double? get wordsPerMinute => wordsPerHr != null ? wordsPerHr! / 60 : null; + + factory Transcript.fromJson(Map json) => Transcript( + text: json['transcript'], + confidence: json['confidence'] <= 100 + ? json['confidence'] + : json['confidence'] / 100, + sttTokens: (json['stt_tokens'] as List) + .map((e) => STTToken.fromJson(e)) + .toList(), + langCode: json['lang_code'], + wordsPerHr: json['words_per_hr'], + ); + Map toJson() => { - "audio_content": base64Encode(audioContent), - "config": config.toJson(), + "transcript": text, + "confidence": confidence, + "stt_tokens": sttTokens.map((e) => e.toJson()).toList(), + "lang_code": langCode, + "words_per_hr": wordsPerHr, }; - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - if (other is! SpeechToTextRequestModel) return false; - - return listEquals(audioContent, other.audioContent) && - config == other.config; - } - - @override - int get hashCode { - final bytesSample = - audioContent.length > 10 ? audioContent.sublist(0, 10) : audioContent; - return Object.hashAll([ - Object.hashAll(bytesSample), - config.hashCode, - ]); - } + Color get color => confidence > 80 ? AppConfig.success : AppConfig.warning; } class STTToken { @@ -94,15 +133,7 @@ class STTToken { int get length => token.text.length; Color color(BuildContext context) { - // turning off the color coding for now - // whisper doesn't include word-level confidence - // if (confidence == null) { return Theme.of(context).colorScheme.onSurface; - // } - // if (confidence! > thresholdForGreen) { - // return AppConfig.success; - // } - // return AppConfig.warning; } factory STTToken.fromJson(Map json) { @@ -147,118 +178,3 @@ class STTToken { ]); } } - -class Transcript { - final String text; - final int confidence; - final List sttTokens; - final String langCode; - final int? wordsPerHr; - - Transcript({ - required this.text, - required this.confidence, - required this.sttTokens, - required this.langCode, - required this.wordsPerHr, - }); - - /// Returns the number of words per minute rounded to one decimal place. - double? get wordsPerMinute => wordsPerHr != null ? wordsPerHr! / 60 : null; - - factory Transcript.fromJson(Map json) => Transcript( - text: json['transcript'], - confidence: json['confidence'] <= 100 - ? json['confidence'] - : json['confidence'] / 100, - sttTokens: (json['stt_tokens'] as List) - .map((e) => STTToken.fromJson(e)) - .toList(), - langCode: json['lang_code'], - wordsPerHr: json['words_per_hr'], - ); - - Map toJson() => { - "transcript": text, - "confidence": confidence, - "stt_tokens": sttTokens.map((e) => e.toJson()).toList(), - "lang_code": langCode, - "words_per_hr": wordsPerHr, - }; - - Color color(BuildContext context) { - if (confidence > thresholdForGreen) { - return AppConfig.success; - } - return AppConfig.warning; - } -} - -class SpeechToTextResult { - final List transcripts; - - SpeechToTextResult({required this.transcripts}); - - factory SpeechToTextResult.fromJson(Map json) => - SpeechToTextResult( - transcripts: (json['transcripts'] as List) - .map((e) => Transcript.fromJson(e)) - .toList(), - ); - - Map toJson() => { - "transcripts": transcripts.map((e) => e.toJson()).toList(), - }; -} - -class SpeechToTextModel { - final List results; - - SpeechToTextModel({ - required this.results, - }); - - Transcript get transcript => results.first.transcripts.first; - - String get langCode => results.first.transcripts.first.langCode; - - factory SpeechToTextModel.fromJson(Map json) { - final results = json['results'] as List; - if (results.isEmpty) { - throw Exception('SpeechToTextModel.fromJson: results is empty'); - } - return SpeechToTextModel( - results: (json['results'] as List) - .map((e) => SpeechToTextResult.fromJson(e)) - .toList(), - ); - } - - Map toJson() => { - "results": results.map((e) => e.toJson()).toList(), - }; - - List constructs( - String roomId, - String eventId, - ) { - final List constructs = []; - final metadata = ConstructUseMetaData( - roomId: roomId, - eventId: eventId, - timeStamp: DateTime.now(), - ); - for (final sstToken in transcript.sttTokens) { - final token = sstToken.token; - if (!token.lemma.saveVocab) continue; - constructs.addAll( - token.allUses( - ConstructUseTypeEnum.pvm, - metadata, - ConstructUseTypeEnum.pvm.pointValue, - ), - ); - } - return constructs; - } -} diff --git a/lib/pangea/toolbar/controllers/speech_to_text_controller.dart b/lib/pangea/toolbar/controllers/speech_to_text_controller.dart deleted file mode 100644 index 0de0a7b3d..000000000 --- a/lib/pangea/toolbar/controllers/speech_to_text_controller.dart +++ /dev/null @@ -1,133 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; - -import 'package:flutter/foundation.dart'; - -import 'package:http/http.dart'; - -import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; -import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; -import 'package:fluffychat/pangea/events/models/representation_content_model.dart'; -import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/toolbar/models/speech_to_text_models.dart'; -import '../../common/config/environment.dart'; -import '../../common/network/requests.dart'; -import '../../common/network/urls.dart'; - -// Assuming SpeechToTextRequestModel, SpeechToTextModel and related models are already defined as in your provided code. - -class _SpeechToTextCacheItem { - Future data; - - _SpeechToTextCacheItem({required this.data}); -} - -class SpeechToTextController { - static final Map _cache = {}; - late final PangeaController _pangeaController; - Timer? _cacheClearTimer; - - SpeechToTextController(this._pangeaController) { - _initializeCacheClearing(); - } - - void _initializeCacheClearing() { - const duration = Duration(minutes: 2); - _cacheClearTimer = Timer.periodic(duration, (Timer t) => _clearCache()); - } - - void _clearCache() { - _cache.clear(); - } - - void dispose() { - _cacheClearTimer?.cancel(); - } - - Future get( - SpeechToTextRequestModel requestModel, - ) async { - final int cacheKey = requestModel.hashCode; - - if (_cache.containsKey(cacheKey)) { - return _cache[cacheKey]!.data; - } else { - final Future response = _fetchResponse( - accessToken: _pangeaController.userController.accessToken, - requestModel: requestModel, - ); - _cache[cacheKey] = _SpeechToTextCacheItem(data: response); - - return response; - } - } - - Future saveSpeechToTextAsRepresentationEvent( - SpeechToTextModel response, - SpeechToTextRequestModel requestModel, - ) { - if (requestModel.audioEvent == null) { - debugPrint( - 'Audio event is null, case of giving speech to text before message sent, currently not implemented', - ); - return Future.value(null); - } - debugPrint('Saving transcript as matrix event'); - - requestModel.audioEvent?.room - .sendPangeaEvent( - content: PangeaRepresentation( - langCode: response.langCode, - text: response.transcript.text, - originalSent: false, - originalWritten: false, - speechToText: response, - ).toJson(), - parentEventId: requestModel.audioEvent!.eventId, - type: PangeaEventTypes.representation, - ) - .then( - (_) => debugPrint('Transcript saved as matrix event'), - ); - - return Future.value(null); - } - - Future _fetchResponse({ - required String accessToken, - required SpeechToTextRequestModel requestModel, - }) async { - final Requests request = Requests( - choreoApiKey: Environment.choreoApiKey, - accessToken: accessToken, - ); - - final Response res = await request.post( - url: PApiUrls.speechToText, - body: requestModel.toJson(), - ); - - if (res.statusCode == 200) { - final Map json = jsonDecode(utf8.decode(res.bodyBytes)); - - final response = SpeechToTextModel.fromJson(json); - - saveSpeechToTextAsRepresentationEvent(response, requestModel).onError( - (error, stackTrace) => ErrorHandler.logError( - e: error, - s: stackTrace, - data: { - "response": response.toJson(), - "requestModel": requestModel.toJson(), - }, - ), - ); - - return response; - } else { - debugPrint('Error converting speech to text: ${res.body}'); - throw Exception('Failed to convert speech to text'); - } - } -} diff --git a/lib/pangea/toolbar/widgets/select_mode_controller.dart b/lib/pangea/toolbar/widgets/select_mode_controller.dart index 013294b52..fd7a760ae 100644 --- a/lib/pangea/toolbar/widgets/select_mode_controller.dart +++ b/lib/pangea/toolbar/widgets/select_mode_controller.dart @@ -9,17 +9,17 @@ import 'package:path_provider/path_provider.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/extensions/pangea_event_extension.dart'; -import 'package:fluffychat/pangea/toolbar/models/speech_to_text_models.dart'; +import 'package:fluffychat/pangea/speech_to_text/speech_to_text_response_model.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_audio_card.dart'; import 'package:fluffychat/pangea/toolbar/widgets/select_mode_buttons.dart'; import 'package:fluffychat/widgets/matrix.dart'; -class _TranscriptionLoader extends AsyncLoader { +class _TranscriptionLoader extends AsyncLoader { final PangeaMessageEvent messageEvent; _TranscriptionLoader(this.messageEvent) : super(); @override - Future fetch() => messageEvent.getSpeechToText( + Future fetch() => messageEvent.getSpeechToText( MatrixState.pangeaController.languageController.userL1!.langCodeShort, MatrixState.pangeaController.languageController.userL2!.langCodeShort, ); @@ -127,7 +127,7 @@ class SelectModeController { ValueNotifier> get translationState => _translationLoader.state; - ValueNotifier> get transcriptionState => + ValueNotifier> get transcriptionState => _transcriptLoader.state; ValueNotifier> get speechTranslationState => diff --git a/lib/pangea/toolbar/widgets/stt_transcript_tokens.dart b/lib/pangea/toolbar/widgets/stt_transcript_tokens.dart index 03247a35f..d25230fd6 100644 --- a/lib/pangea/toolbar/widgets/stt_transcript_tokens.dart +++ b/lib/pangea/toolbar/widgets/stt_transcript_tokens.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/message_token_text/tokens_util.dart'; -import 'package:fluffychat/pangea/toolbar/models/speech_to_text_models.dart'; +import 'package:fluffychat/pangea/speech_to_text/speech_to_text_response_model.dart'; class SttTranscriptTokens extends StatelessWidget { - final SpeechToTextModel model; + final SpeechToTextResponseModel model; final TextStyle? style; final void Function(PangeaToken)? onClick; From 19d11994d6bbf7fd6f95d3603e3b597708b1af5a Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 3 Dec 2025 13:58:46 -0500 Subject: [PATCH 2/9] removed message data controller --- lib/pangea/choreographer/choreographer.dart | 11 +- .../common/controllers/pangea_controller.dart | 3 - .../controllers/message_data_controller.dart | 198 ------------------ .../event_wrappers/pangea_message_event.dart | 58 +++-- .../pangea_representation_event.dart | 65 ++++-- .../events/models/stt_translation_model.dart | 13 +- 6 files changed, 111 insertions(+), 237 deletions(-) delete mode 100644 lib/pangea/events/controllers/message_data_controller.dart diff --git a/lib/pangea/choreographer/choreographer.dart b/lib/pangea/choreographer/choreographer.dart index 210a8ed16..8c936a3d7 100644 --- a/lib/pangea/choreographer/choreographer.dart +++ b/lib/pangea/choreographer/choreographer.dart @@ -14,10 +14,10 @@ import 'package:fluffychat/pangea/choreographer/it/completed_it_step_model.dart' import 'package:fluffychat/pangea/choreographer/pangea_message_content_model.dart'; import 'package:fluffychat/pangea/choreographer/text_editing/edit_type_enum.dart'; import 'package:fluffychat/pangea/choreographer/text_editing/pangea_text_controller.dart'; -import 'package:fluffychat/pangea/events/controllers/message_data_controller.dart'; import 'package:fluffychat/pangea/events/models/representation_content_model.dart'; import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/events/repo/token_api_models.dart'; +import 'package:fluffychat/pangea/events/repo/tokens_repo.dart'; import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; import 'package:fluffychat/pangea/spaces/models/space_model.dart'; import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart'; @@ -265,15 +265,14 @@ class Choreographer extends ChangeNotifier { final l1LangCode = MatrixState.pangeaController.languageController.userL1?.langCode; if (l1LangCode != null && l2LangCode != null) { - final res = await MessageDataController.getTokens( - repEventId: null, - room: null, - req: TokensRequestModel( + final res = await TokensRepo.get( + MatrixState.pangeaController.userController.accessToken, + TokensRequestModel( fullText: message, senderL1: l1LangCode, senderL2: l2LangCode, ), - ).timeout(const Duration(seconds: 10)); + ); tokensResp = res.isValue ? res.result : null; } diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index f61209428..2595b1819 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -21,7 +21,6 @@ import 'package:fluffychat/pangea/learning_settings/controllers/language_control import 'package:fluffychat/pangea/learning_settings/utils/locale_provider.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; import 'package:fluffychat/pangea/spaces/controllers/space_code_controller.dart'; -import 'package:fluffychat/pangea/speech_to_text/speech_to_text_controller.dart'; import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart'; import 'package:fluffychat/pangea/toolbar/controllers/text_to_speech_controller.dart'; import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart'; @@ -40,7 +39,6 @@ class PangeaController { late PutAnalyticsController putAnalytics; late SubscriptionController subscriptionController; late TextToSpeechController textToSpeech; - late SpeechToTextController speechToText; ///store Services final pLanguageStore = PLanguageStore(); @@ -85,7 +83,6 @@ class PangeaController { putAnalytics = PutAnalyticsController(this); subscriptionController = SubscriptionController(this); textToSpeech = TextToSpeechController(this); - speechToText = SpeechToTextController(this); PAuthGaurd.pController = this; } diff --git a/lib/pangea/events/controllers/message_data_controller.dart b/lib/pangea/events/controllers/message_data_controller.dart deleted file mode 100644 index 30319ce7c..000000000 --- a/lib/pangea/events/controllers/message_data_controller.dart +++ /dev/null @@ -1,198 +0,0 @@ -import 'dart:async'; - -import 'package:async/async.dart'; -import 'package:matrix/matrix.dart' hide Result; - -import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; -import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; -import 'package:fluffychat/pangea/events/models/representation_content_model.dart'; -import 'package:fluffychat/pangea/events/models/stt_translation_model.dart'; -import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart'; -import 'package:fluffychat/pangea/events/repo/token_api_models.dart'; -import 'package:fluffychat/pangea/events/repo/tokens_repo.dart'; -import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/translation/full_text_translation_repo.dart'; -import 'package:fluffychat/pangea/translation/full_text_translation_request_model.dart'; -import 'package:fluffychat/widgets/future_loading_dialog.dart'; -import 'package:fluffychat/widgets/matrix.dart'; - -// TODO - make this static and take it out of the _pangeaController -// will need to pass accessToken to the requests -class MessageDataController { - /// get tokens from the server - /// if repEventId is not null, send the tokens to the room - static Future> getTokens({ - required String? repEventId, - required TokensRequestModel req, - required Room? room, - }) async { - final res = await TokensRepo.get( - MatrixState.pangeaController.userController.accessToken, - req, - ); - if (res.isValue && repEventId != null && room != null) { - room - .sendPangeaEvent( - content: PangeaMessageTokens( - tokens: res.result!.tokens, - detections: res.result!.detections, - ).toJson(), - parentEventId: repEventId, - type: PangeaEventTypes.tokens, - ) - .catchError( - (e) => ErrorHandler.logError( - m: "error in _getTokens.sendPangeaEvent", - e: e, - s: StackTrace.current, - data: req.toJson(), - ), - ); - } - return res; - } - - /////// translation //////// - - /// get translation from the server - /// if in cache, return from cache - /// if not in cache, get from server - /// send the translation to the room as a representation event - static Future getPangeaRepresentation({ - required FullTextTranslationRequestModel req, - required Event messageEvent, - }) => - _getPangeaRepresentation(req: req, messageEvent: messageEvent); - - static Future _getPangeaRepresentation({ - required FullTextTranslationRequestModel req, - required Event messageEvent, - }) async { - final res = await FullTextTranslationRepo.get( - MatrixState.pangeaController.userController.accessToken, - req, - ); - - if (res.isError) { - throw res.error!; - } - - final rep = PangeaRepresentation( - langCode: req.tgtLang, - text: res.result!, - originalSent: false, - originalWritten: false, - ); - - messageEvent.room - .sendPangeaEvent( - content: rep.toJson(), - parentEventId: messageEvent.eventId, - type: PangeaEventTypes.representation, - ) - .catchError( - (e) => ErrorHandler.logError( - m: "error in _getPangeaRepresentation.sendPangeaEvent", - e: e, - s: StackTrace.current, - data: req.toJson(), - ), - ); - - return rep; - } - - static Future getPangeaRepresentationEvent({ - required FullTextTranslationRequestModel req, - required PangeaMessageEvent messageEvent, - bool originalSent = false, - }) async { - final res = await FullTextTranslationRepo.get( - MatrixState.pangeaController.userController.accessToken, - req, - ); - - if (res.isError) { - return null; - } - - if (originalSent && messageEvent.originalSent != null) { - originalSent = false; - } - - final rep = PangeaRepresentation( - langCode: req.tgtLang, - text: res.result!, - originalSent: originalSent, - originalWritten: false, - ); - - try { - final repEvent = await messageEvent.room.sendPangeaEvent( - content: rep.toJson(), - parentEventId: messageEvent.eventId, - type: PangeaEventTypes.representation, - ); - return repEvent?.eventId; - } catch (e, s) { - ErrorHandler.logError( - m: "error in _getPangeaRepresentation.sendPangeaEvent", - e: e, - s: s, - data: req.toJson(), - ); - return null; - } - } - - static Future getSttTranslation({ - required String? repEventId, - required FullTextTranslationRequestModel req, - required Room? room, - }) => - _getSttTranslation( - repEventId: repEventId, - req: req, - room: room, - ); - - static Future _getSttTranslation({ - required String? repEventId, - required FullTextTranslationRequestModel req, - required Room? room, - }) async { - final res = await FullTextTranslationRepo.get( - MatrixState.pangeaController.userController.accessToken, - req, - ); - - if (res.isError) { - throw res.error!; - } - - final translation = SttTranslationModel( - translation: res.result!, - langCode: req.tgtLang, - ); - - if (repEventId != null && room != null) { - room - .sendPangeaEvent( - content: translation.toJson(), - parentEventId: repEventId, - type: PangeaEventTypes.sttTranslation, - ) - .catchError( - (e) => ErrorHandler.logError( - m: "error in _getSttTranslation.sendPangeaEvent", - e: e, - s: StackTrace.current, - data: req.toJson(), - ), - ); - } - - return translation; - } -} diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index be1643194..cbdc481f2 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -10,20 +10,22 @@ import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:fluffychat/pangea/choreographer/choreo_record_model.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; -import 'package:fluffychat/pangea/events/controllers/message_data_controller.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_representation_event.dart'; import 'package:fluffychat/pangea/events/models/representation_content_model.dart'; import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/events/repo/language_detection_repo.dart'; import 'package:fluffychat/pangea/events/repo/language_detection_request.dart'; import 'package:fluffychat/pangea/events/repo/language_detection_response.dart'; +import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; import 'package:fluffychat/pangea/spaces/models/space_model.dart'; import 'package:fluffychat/pangea/speech_to_text/audio_encoding_enum.dart'; +import 'package:fluffychat/pangea/speech_to_text/speech_to_text_repo.dart'; import 'package:fluffychat/pangea/speech_to_text/speech_to_text_request_model.dart'; import 'package:fluffychat/pangea/speech_to_text/speech_to_text_response_model.dart'; import 'package:fluffychat/pangea/toolbar/controllers/text_to_speech_controller.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_audio_card.dart'; +import 'package:fluffychat/pangea/translation/full_text_translation_repo.dart'; import 'package:fluffychat/pangea/translation/full_text_translation_request_model.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import '../../../widgets/matrix.dart'; @@ -305,15 +307,13 @@ class PangeaMessageEvent { final matrixFile = await _event.downloadAndDecryptAttachment(); - final SpeechToTextResponseModel response = - await MatrixState.pangeaController.speechToText.get( + final result = await SpeechToTextRepo.get( + MatrixState.pangeaController.userController.accessToken, SpeechToTextRequestModel( audioContent: matrixFile.bytes, audioEvent: _event, config: SpeechToTextAudioConfigModel( encoding: mimeTypeToAudioEncoding(matrixFile.mimeType), - //this is the default in the RecordConfig in record package - //TODO: check if this is the correct value and make it a constant somewhere sampleRateHertz: 22050, userL1: l1Code, userL2: l2Code, @@ -321,6 +321,13 @@ class PangeaMessageEvent { ), ); + if (result.error != null) { + throw Exception( + "Error getting speech to text: ${result.error}", + ); + } + + final SpeechToTextResponseModel response = result.result!; _representations?.add( RepresentationEvent( timeline: timeline, @@ -540,17 +547,29 @@ class PangeaMessageEvent { // clear representations cache so the new representation event can be added when next requested _representations = null; - return MessageDataController.getPangeaRepresentationEvent( - req: FullTextTranslationRequestModel( + final res = await FullTextTranslationRepo.get( + MatrixState.pangeaController.userController.accessToken, + FullTextTranslationRequestModel( text: originalSent?.content.text ?? _latestEdit.body, srcLang: originalSent?.langCode, tgtLang: langCode, userL2: l2Code ?? LanguageKeys.unknownLanguage, userL1: l1Code ?? LanguageKeys.unknownLanguage, ), - messageEvent: this, - originalSent: true, ); + + if (res.isError) return null; + final repEvent = await room.sendPangeaEvent( + content: PangeaRepresentation( + langCode: langCode, + text: res.result!, + originalSent: originalSent == null, + originalWritten: false, + ).toJson(), + parentEventId: eventId, + type: PangeaEventTypes.representation, + ); + return repEvent?.eventId; } Future l1Respresentation() async { @@ -584,17 +603,30 @@ class PangeaMessageEvent { // clear representations cache so the new representation event can be added when next requested _representations = null; - final resp = await MessageDataController.getPangeaRepresentation( - req: FullTextTranslationRequestModel( + + final resp = await FullTextTranslationRepo.get( + MatrixState.pangeaController.userController.accessToken, + FullTextTranslationRequestModel( text: includedIT ? originalWrittenContent : messageDisplayText, srcLang: srcLang, tgtLang: l1Code!, userL2: l2Code!, userL1: l1Code!, ), - messageEvent: _event, ); - return resp.text; + + if (resp.isError) throw resp.error!; + room.sendPangeaEvent( + content: PangeaRepresentation( + langCode: l1Code!, + text: resp.result!, + originalSent: false, + originalWritten: false, + ).toJson(), + parentEventId: eventId, + type: PangeaEventTypes.representation, + ); + return resp.result!; } RepresentationEvent? get originalSent => representations diff --git a/lib/pangea/events/event_wrappers/pangea_representation_event.dart b/lib/pangea/events/event_wrappers/pangea_representation_event.dart index 6b5f81a75..06b28c16f 100644 --- a/lib/pangea/events/event_wrappers/pangea_representation_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_representation_event.dart @@ -14,7 +14,6 @@ import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart'; import 'package:fluffychat/pangea/choreographer/choreo_record_model.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; -import 'package:fluffychat/pangea/events/controllers/message_data_controller.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_choreo_event.dart'; import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart'; import 'package:fluffychat/pangea/events/models/language_detection_model.dart'; @@ -23,10 +22,13 @@ import 'package:fluffychat/pangea/events/models/representation_content_model.dar import 'package:fluffychat/pangea/events/models/stt_translation_model.dart'; import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/events/repo/token_api_models.dart'; +import 'package:fluffychat/pangea/events/repo/tokens_repo.dart'; +import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; import 'package:fluffychat/pangea/morphs/morph_features_enum.dart'; import 'package:fluffychat/pangea/morphs/parts_of_speech_enum.dart'; import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart'; +import 'package:fluffychat/pangea/translation/full_text_translation_repo.dart'; import 'package:fluffychat/pangea/translation/full_text_translation_request_model.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -113,23 +115,31 @@ class RepresentationEvent { ), ); } - final res = await MessageDataController.getTokens( - repEventId: _event?.eventId, - room: _event?.room ?? parentMessageEvent.room, - req: TokensRequestModel( + final res = await TokensRepo.get( + MatrixState.pangeaController.userController.accessToken, + TokensRequestModel( fullText: text, langCode: langCode, senderL1: MatrixState.pangeaController.languageController.userL1?.langCode ?? LanguageKeys.unknownLanguage, - // since langCode is known, senderL2 will be used to determine whether these tokens - // need pos/mporph tags whether lemmas are eligible to marked as "save_vocab=true" senderL2: MatrixState.pangeaController.languageController.userL2?.langCode ?? LanguageKeys.unknownLanguage, ), ); + if (_event != null) { + _event!.room.sendPangeaEvent( + content: PangeaMessageTokens( + tokens: res.result!.tokens, + detections: res.result!.detections, + ).toJson(), + parentEventId: _event!.eventId, + type: PangeaEventTypes.tokens, + ); + } + if (res.isError) { return Result.error(res.error!); } else { @@ -152,16 +162,25 @@ class RepresentationEvent { return; } - await MessageDataController.getTokens( - repEventId: repEventID, - room: room, - req: TokensRequestModel( + final resp = await TokensRepo.get( + MatrixState.pangeaController.userController.accessToken, + TokensRequestModel( fullText: text, langCode: langCode, senderL1: userl1, senderL2: userl2, ), ); + + if (resp.isError) return; + room.sendPangeaEvent( + content: PangeaMessageTokens( + tokens: resp.result!.tokens, + detections: resp.result!.detections, + ).toJson(), + parentEventId: _event!.eventId, + type: PangeaEventTypes.tokens, + ); } List get sttTranslations { @@ -217,16 +236,32 @@ class RepresentationEvent { final local = sttTranslations.firstWhereOrNull((t) => t.langCode == userL1); if (local != null) return local; - return MessageDataController.getSttTranslation( - repEventId: _event?.eventId, - room: _event?.room, - req: FullTextTranslationRequestModel( + final res = await FullTextTranslationRepo.get( + MatrixState.pangeaController.userController.accessToken, + FullTextTranslationRequestModel( text: content.speechToText!.transcript.text, tgtLang: userL1, userL2: userL2, userL1: userL1, ), ); + + if (res.isError) { + throw res.error!; + } + + final translation = SttTranslationModel( + translation: res.result!, + langCode: userL1, + ); + + _event?.room.sendPangeaEvent( + content: translation.toJson(), + parentEventId: _event!.eventId, + type: PangeaEventTypes.sttTranslation, + ); + + return translation; } ChoreoRecordModel? get choreo { diff --git a/lib/pangea/events/models/stt_translation_model.dart b/lib/pangea/events/models/stt_translation_model.dart index 47f31be4f..06becba21 100644 --- a/lib/pangea/events/models/stt_translation_model.dart +++ b/lib/pangea/events/models/stt_translation_model.dart @@ -1,3 +1,7 @@ +import 'package:matrix/matrix.dart'; + +import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; + class SttTranslationModel { final String translation; final String langCode; @@ -8,9 +12,14 @@ class SttTranslationModel { }); factory SttTranslationModel.fromJson(Map json) { + final content = json.tryGetMap(PangeaEventTypes.sttTranslation); + if (content == null) { + throw Exception("STT Translation content is null"); + } + return SttTranslationModel( - translation: json['translation'] as String, - langCode: json['lang_code'] as String, + translation: content['translation'] as String, + langCode: content['lang_code'] as String, ); } From 9cb155fcf1aa3ddabc5caac4c4e63efc92589799 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 3 Dec 2025 15:22:45 -0500 Subject: [PATCH 3/9] reorganize pangea rep / pangea message event files --- lib/pages/chat/chat.dart | 2 +- .../activity_summary_analytics_model.dart | 2 +- .../event_wrappers/pangea_message_event.dart | 811 ++++++++---------- .../pangea_representation_event.dart | 392 ++++----- .../events/repo/language_detection_repo.dart | 91 +- .../repo/language_detection_request.dart | 12 + .../toolbar/widgets/message_audio_card.dart | 15 +- .../widgets/message_selection_overlay.dart | 10 +- .../widgets/select_mode_controller.dart | 26 +- 9 files changed, 609 insertions(+), 752 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 4bc911fa8..199392716 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -2149,7 +2149,7 @@ class ChatController extends State ownMessage: true, ); - final stt = await messageEvent.getSpeechToText( + final stt = await messageEvent.requestSpeechToText( MatrixState.pangeaController.languageController.userL1?.langCodeShort ?? LanguageKeys.unknownLanguage, MatrixState.pangeaController.languageController.userL2?.langCodeShort ?? diff --git a/lib/pangea/activity_summary/activity_summary_analytics_model.dart b/lib/pangea/activity_summary/activity_summary_analytics_model.dart index dc6b80581..32344a7c4 100644 --- a/lib/pangea/activity_summary/activity_summary_analytics_model.dart +++ b/lib/pangea/activity_summary/activity_summary_analytics_model.dart @@ -45,7 +45,7 @@ class ActivitySummaryAnalyticsModel { } void addMessageConstructs(PangeaMessageEvent event) { - final uses = event.originalSent?.vocabAndMorphUses(); + final uses = event.originalSent?.vocabAndMorphUses; if (uses == null || uses.isEmpty) return; addConstructs(event.senderId, uses); } diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index cbdc481f2..230974851 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -4,13 +4,14 @@ import 'dart:ui'; import 'package:flutter/foundation.dart'; +import 'package:async/async.dart'; import 'package:collection/collection.dart'; -import 'package:matrix/matrix.dart'; -import 'package:sentry_flutter/sentry_flutter.dart'; +import 'package:matrix/matrix.dart' hide Result; import 'package:fluffychat/pangea/choreographer/choreo_record_model.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_representation_event.dart'; +import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart'; import 'package:fluffychat/pangea/events/models/representation_content_model.dart'; import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/events/repo/language_detection_repo.dart'; @@ -72,12 +73,11 @@ class PangeaMessageEvent { bool get isAudioMessage => _event.messageType == MessageTypes.Audio; - String? get mimetype { - if (!isAudioMessage) return null; - final Map? info = _event.content.tryGetMap("info"); - if (info == null) return null; - return info["mime_type"] ?? info["mimetype"]; - } + String? get _l2Code => + MatrixState.pangeaController.languageController.activeL2Code(); + + String? get _l1Code => + MatrixState.pangeaController.languageController.userL1?.langCode; Event? _latestEditCache; Event get _latestEdit => _latestEditCache ??= _event @@ -92,131 +92,6 @@ class PangeaMessageEvent { .firstOrNull ?? _event; - void updateLatestEdit() { - _latestEditCache = null; - _representations = null; - } - - Future getMatrixAudioFile( - String langCode, - ) async { - final RepresentationEvent? rep = representationByLanguage(langCode); - final tokensResp = await rep?.tokensGlobal( - senderId, - originServerTs, - ); - - final TextToSpeechRequest params = TextToSpeechRequest( - text: rep?.content.text ?? body, - tokens: tokensResp?.result?.map((t) => t.text).toList() ?? [], - langCode: langCode, - userL1: l1Code ?? LanguageKeys.unknownLanguage, - userL2: l2Code ?? LanguageKeys.unknownLanguage, - ); - - final TextToSpeechResponse response = - await MatrixState.pangeaController.textToSpeech.get( - params, - ); - - final audioBytes = base64.decode(response.audioContent); - final eventIdParam = _event.eventId; - final fileName = - "audio_for_${eventIdParam}_$langCode.${response.fileExtension}"; - - final file = PangeaAudioFile( - bytes: audioBytes, - name: fileName, - mimeType: response.mimeType, - duration: response.durationMillis, - waveform: response.waveform, - tokens: response.ttsTokens, - ); - - sendAudioEvent(file, response, rep?.text ?? body, langCode); - - return file; - } - - Future sendAudioEvent( - PangeaAudioFile file, - TextToSpeechResponse response, - String text, - String langCode, - ) async { - final String? eventId = await room.sendFileEvent( - file, - inReplyTo: _event, - extraContent: { - 'info': { - ...file.info, - 'duration': response.durationMillis, - }, - 'org.matrix.msc3245.voice': {}, - 'org.matrix.msc1767.audio': { - 'duration': response.durationMillis, - 'waveform': response.waveform, - }, - ModelKey.transcription: - response.toPangeaAudioEventData(text, langCode).toJson(), - }, - ); - - debugPrint("eventId in getTextToSpeechGlobal $eventId"); - final Event? audioEvent = - eventId != null ? await room.getEventById(eventId) : null; - - if (audioEvent == null) { - return null; - } - allAudio.add(audioEvent); - return audioEvent; - } - - Event? getTextToSpeechLocal(String langCode, String text) { - return allAudio.firstWhereOrNull( - (event) { - try { - // Safely access - final dataMap = event.content.tryGetMap(ModelKey.transcription); - - if (dataMap == null) { - return false; - } - - // old text to speech content will not have TTSToken data - // we want to disregard them and just generate new ones - // for that, we'll return false if 'tokens' are null - // while in-development, we'll pause here to inspect - // debugger can be removed after we're sure it's working - if (dataMap['tokens'] == null) { - // events before today will definitely not have the tokens - debugger( - when: kDebugMode && - event.originServerTs.isAfter(DateTime(2024, 10, 16)), - ); - return false; - } - - final PangeaAudioEventData audioData = - PangeaAudioEventData.fromJson(dataMap as dynamic); - - // Check if both language code and text match - return audioData.langCode == langCode && audioData.text == text; - } catch (e, s) { - debugger(when: kDebugMode); - ErrorHandler.logError( - e: e, - s: s, - data: {}, - m: "error parsing data in getTextToSpeechLocal", - ); - return false; - } - }, - ); - } - // get audio events that are related to this event Set get allAudio => _latestEdit .aggregatedEvents( @@ -230,159 +105,25 @@ class PangeaMessageEvent { null; }).toSet(); - SpeechToTextResponseModel? getSpeechToTextLocal() { - final rawBotTranscription = - event.content.tryGetMap(ModelKey.botTranscription); - - if (rawBotTranscription != null) { - try { - return SpeechToTextResponseModel.fromJson( - Map.from(rawBotTranscription), - ); - } catch (err, s) { - ErrorHandler.logError( - e: err, - s: s, - data: { - "event": _event.toJson(), - }, - m: "error parsing botTranscription", - ); - return null; - } - } - - return representations - .firstWhereOrNull( - (element) => element.content.speechToText != null, - ) - ?.content - .speechToText; - } - - Future getSpeechToText( - String l1Code, - String l2Code, - ) async { - if (!isAudioMessage) { - throw 'Calling getSpeechToText on non-audio message'; - } - - final rawBotTranscription = - event.content.tryGetMap(ModelKey.botTranscription); - if (rawBotTranscription != null) { - final SpeechToTextResponseModel botTranscription = - SpeechToTextResponseModel.fromJson( - Map.from(rawBotTranscription), - ); - - _representations ??= []; - _representations!.add( - RepresentationEvent( - timeline: timeline, - parentMessageEvent: _event, - content: PangeaRepresentation( - langCode: botTranscription.langCode, - text: botTranscription.transcript.text, - originalSent: false, - originalWritten: false, - speechToText: botTranscription, - ), - ), - ); - - return botTranscription; - } - - final SpeechToTextResponseModel? speechToTextLocal = representations - .firstWhereOrNull( - (element) => element.content.speechToText != null, - ) - ?.content - .speechToText; - - if (speechToTextLocal != null) { - return speechToTextLocal; - } - - final matrixFile = await _event.downloadAndDecryptAttachment(); - - final result = await SpeechToTextRepo.get( - MatrixState.pangeaController.userController.accessToken, - SpeechToTextRequestModel( - audioContent: matrixFile.bytes, - audioEvent: _event, - config: SpeechToTextAudioConfigModel( - encoding: mimeTypeToAudioEncoding(matrixFile.mimeType), - sampleRateHertz: 22050, - userL1: l1Code, - userL2: l2Code, - ), - ), - ); - - if (result.error != null) { - throw Exception( - "Error getting speech to text: ${result.error}", - ); - } - - final SpeechToTextResponseModel response = result.result!; - _representations?.add( - RepresentationEvent( - timeline: timeline, - parentMessageEvent: _event, - content: PangeaRepresentation( - langCode: response.langCode, - text: response.transcript.text, - originalSent: false, - originalWritten: false, - speechToText: response, - ), - ), - ); - - return response; - } - - Future sttTranslationByLanguageGlobal({ - required String langCode, - required String l1Code, - required String l2Code, - }) async { - if (!representations.any( - (element) => element.content.speechToText != null, - )) { - await getSpeechToText(l1Code, l2Code); - } - - final rep = representations.firstWhereOrNull( - (element) => element.content.speechToText != null, - ); - - if (rep == null) { - throw Exception("No speech to text representation found"); - } - - final resp = await rep.getSttTranslation(userL1: l1Code, userL2: l2Code); - return resp.translation; - } - - PangeaMessageTokens? _tokensSafe(Map? content) { - try { - if (content == null) return null; - return PangeaMessageTokens.fromJson(content); - } catch (e, s) { - debugger(when: kDebugMode); - ErrorHandler.logError( - e: e, - s: s, - data: content ?? {}, - m: "error parsing tokensSent", - ); - return null; - } - } + List get _repEvents => _latestEdit + .aggregatedEvents( + timeline, + PangeaEventTypes.representation, + ) + .map( + (e) => RepresentationEvent( + event: e, + parentMessageEvent: _event, + timeline: timeline, + ), + ) + .sorted( + (a, b) { + if (a.event == null) return -1; + if (b.event == null) return 1; + return b.event!.originServerTs.compareTo(a.event!.originServerTs); + }, + ).toList(); ChoreoRecordModel? get _embeddedChoreo { try { @@ -402,6 +143,22 @@ class PangeaMessageEvent { } } + PangeaMessageTokens? _tokensSafe(Map? content) { + try { + if (content == null) return null; + return PangeaMessageTokens.fromJson(content); + } catch (e, s) { + debugger(when: kDebugMode); + ErrorHandler.logError( + e: e, + s: s, + data: content ?? {}, + m: "error parsing tokensSent", + ); + return null; + } + } + List? _representations; List get representations { if (_representations != null) return _representations!; @@ -419,18 +176,6 @@ class PangeaMessageEvent { choreo: _embeddedChoreo, timeline: timeline, ); - if (_latestEdit.content[ModelKey.choreoRecord] == null) { - Sentry.addBreadcrumb( - Breadcrumb( - message: "originalSent created without _event or _choreo", - data: { - "eventId": _latestEdit.eventId, - "room": _latestEdit.room.id, - "sender": _latestEdit.senderId, - }, - ), - ); - } // If originalSent has no tokens, there is not way to generate a tokens event // and send it as a related event, since original sent has not eventID to set @@ -480,155 +225,10 @@ class PangeaMessageEvent { } } - _representations!.addAll( - _latestEdit - .aggregatedEvents( - timeline, - PangeaEventTypes.representation, - ) - .map( - (e) => RepresentationEvent( - event: e, - parentMessageEvent: _event, - timeline: timeline, - ), - ) - .sorted( - (a, b) { - //TODO - test with edited events to make sure this is working - if (a.event == null) return -1; - if (b.event == null) return 1; - return b.event!.originServerTs.compareTo(a.event!.originServerTs); - }, - ).toList(), - ); - + _representations!.addAll(_repEvents); return _representations!; } - RepresentationEvent? representationByLanguage( - String langCode, { - bool Function(RepresentationEvent)? filter, - }) => - representations.firstWhereOrNull( - (element) => - element.langCode.split("-")[0] == langCode.split("-")[0] && - (filter?.call(element) ?? true), - ); - - Future representationByDetectedLanguage() async { - LanguageDetectionResponse? resp; - try { - resp = await LanguageDetectionRepo.get( - MatrixState.pangeaController.userController.accessToken, - request: LanguageDetectionRequest( - text: _latestEdit.body, - senderl1: l1Code, - senderl2: l2Code, - ), - ); - } catch (e, s) { - ErrorHandler.logError( - e: e, - s: s, - data: { - "event": _event.toJson(), - }, - ); - return null; - } - - final langCode = resp.detections.firstOrNull?.langCode; - if (langCode == null) return null; - if (langCode == originalSent?.langCode) { - return originalSent?.event?.eventId; - } - - // clear representations cache so the new representation event can be added when next requested - _representations = null; - - final res = await FullTextTranslationRepo.get( - MatrixState.pangeaController.userController.accessToken, - FullTextTranslationRequestModel( - text: originalSent?.content.text ?? _latestEdit.body, - srcLang: originalSent?.langCode, - tgtLang: langCode, - userL2: l2Code ?? LanguageKeys.unknownLanguage, - userL1: l1Code ?? LanguageKeys.unknownLanguage, - ), - ); - - if (res.isError) return null; - final repEvent = await room.sendPangeaEvent( - content: PangeaRepresentation( - langCode: langCode, - text: res.result!, - originalSent: originalSent == null, - originalWritten: false, - ).toJson(), - parentEventId: eventId, - type: PangeaEventTypes.representation, - ); - return repEvent?.eventId; - } - - Future l1Respresentation() async { - if (l1Code == null || l2Code == null) { - throw Exception("Missing language codes"); - } - - final includedIT = - (originalSent?.choreo?.endedWithIT(originalSent!.text) ?? false) && - !(originalSent?.choreo?.includedIGC ?? true); - - RepresentationEvent? rep; - if (!includedIT) { - // if the message didn't go through translation, get any l1 rep - rep = representationByLanguage(l1Code!); - } else { - // if the message went through translation, get the non-original - // l1 rep since originalWritten could contain some l2 words - // (https://github.com/pangeachat/client/issues/3591) - rep = representationByLanguage( - l1Code!, - filter: (rep) => !rep.content.originalWritten, - ); - } - - if (rep != null) return rep.content.text; - - final String srcLang = includedIT - ? (originalWritten?.langCode ?? l1Code!) - : (originalSent?.langCode ?? l2Code!); - - // clear representations cache so the new representation event can be added when next requested - _representations = null; - - final resp = await FullTextTranslationRepo.get( - MatrixState.pangeaController.userController.accessToken, - FullTextTranslationRequestModel( - text: includedIT ? originalWrittenContent : messageDisplayText, - srcLang: srcLang, - tgtLang: l1Code!, - userL2: l2Code!, - userL1: l1Code!, - ), - ); - - if (resp.isError) throw resp.error!; - room.sendPangeaEvent( - content: PangeaRepresentation( - langCode: l1Code!, - text: resp.result!, - originalSent: false, - originalWritten: false, - ).toJson(), - parentEventId: eventId, - type: PangeaEventTypes.representation, - ); - return resp.result!; - } - RepresentationEvent? get originalSent => representations .firstWhereOrNull((element) => element.content.originalSent); @@ -646,12 +246,6 @@ class PangeaMessageEvent { return written ?? body; } - String? get l2Code => - MatrixState.pangeaController.languageController.activeL2Code(); - - String? get l1Code => - MatrixState.pangeaController.languageController.userL1?.langCode; - String get messageDisplayLangCode { if (isAudioMessage) { final stt = getSpeechToTextLocal(); @@ -665,7 +259,7 @@ class PangeaMessageEvent { final String? originalLangCode = originalSent?.langCode; - final String? langCode = immersionMode ? l2Code : originalLangCode; + final String? langCode = immersionMode ? _l2Code : originalLangCode; return langCode ?? LanguageKeys.unknownLanguage; } @@ -681,4 +275,319 @@ class PangeaMessageEvent { PLanguageStore.rtlLanguageCodes.contains(messageDisplayLangCode) ? TextDirection.rtl : TextDirection.ltr; + + void updateLatestEdit() { + _latestEditCache = null; + _representations = null; + } + + RepresentationEvent? representationByLanguage( + String langCode, { + bool Function(RepresentationEvent)? filter, + }) => + representations.firstWhereOrNull( + (element) => + element.langCode.split("-")[0] == langCode.split("-")[0] && + (filter?.call(element) ?? true), + ); + + Event? getTextToSpeechLocal(String langCode, String text) { + for (final audio in allAudio) { + final dataMap = audio.content.tryGetMap(ModelKey.transcription); + if (dataMap == null || !dataMap.containsKey('tokens')) continue; + + try { + final PangeaAudioEventData audioData = PangeaAudioEventData.fromJson( + dataMap as dynamic, + ); + + if (audioData.langCode == langCode && audioData.text == text) { + return audio; + } + } catch (e, s) { + debugger(when: kDebugMode); + ErrorHandler.logError( + e: e, + s: s, + data: { + "event": audio.toJson(), + }, + m: "error parsing data in getTextToSpeechLocal", + ); + } + } + return null; + } + + SpeechToTextResponseModel? getSpeechToTextLocal() { + final rawBotTranscription = + event.content.tryGetMap(ModelKey.botTranscription); + + if (rawBotTranscription != null) { + try { + return SpeechToTextResponseModel.fromJson( + Map.from(rawBotTranscription), + ); + } catch (err, s) { + ErrorHandler.logError( + e: err, + s: s, + data: { + "event": _event.toJson(), + }, + m: "error parsing botTranscription", + ); + return null; + } + } + + return representations + .firstWhereOrNull( + (element) => element.content.speechToText != null, + ) + ?.content + .speechToText; + } + + Future requestTextToSpeech( + String langCode, + ) async { + final local = getTextToSpeechLocal(langCode, messageDisplayText); + if (local != null) { + final file = await local.getPangeaAudioFile(); + if (file != null) return file; + } + + final rep = representationByLanguage(langCode); + final tokensResp = await rep?.requestTokens(); + final request = TextToSpeechRequest( + text: rep?.content.text ?? body, + tokens: tokensResp?.result?.map((t) => t.text).toList() ?? [], + langCode: langCode, + userL1: _l1Code ?? LanguageKeys.unknownLanguage, + userL2: _l2Code ?? LanguageKeys.unknownLanguage, + ); + + final response = await MatrixState.pangeaController.textToSpeech.get( + request, + ); + + final audioBytes = base64.decode(response.audioContent); + final fileName = + "audio_for_${_event.eventId}_$langCode.${response.fileExtension}"; + + final file = PangeaAudioFile( + bytes: audioBytes, + name: fileName, + mimeType: response.mimeType, + duration: response.durationMillis, + waveform: response.waveform, + tokens: response.ttsTokens, + ); + + room.sendFileEvent( + file, + inReplyTo: _event, + extraContent: { + 'info': { + ...file.info, + 'duration': response.durationMillis, + }, + 'org.matrix.msc3245.voice': {}, + 'org.matrix.msc1767.audio': { + 'duration': response.durationMillis, + 'waveform': response.waveform, + }, + ModelKey.transcription: response + .toPangeaAudioEventData(rep?.text ?? body, langCode) + .toJson(), + }, + ).then((eventId) async { + final Event? audioEvent = + eventId != null ? await room.getEventById(eventId) : null; + + if (audioEvent != null) { + allAudio.add(audioEvent); + } + }); + + return file; + } + + Future requestSpeechToText( + String l1Code, + String l2Code, + ) async { + if (!isAudioMessage) { + throw 'Calling getSpeechToText on non-audio message'; + } + + final speechToTextLocal = getSpeechToTextLocal(); + if (speechToTextLocal != null) { + return speechToTextLocal; + } + + final matrixFile = await _event.downloadAndDecryptAttachment(); + final result = await SpeechToTextRepo.get( + MatrixState.pangeaController.userController.accessToken, + SpeechToTextRequestModel( + audioContent: matrixFile.bytes, + audioEvent: _event, + config: SpeechToTextAudioConfigModel( + encoding: mimeTypeToAudioEncoding(matrixFile.mimeType), + sampleRateHertz: 22050, + userL1: l1Code, + userL2: l2Code, + ), + ), + ); + + if (result.error != null) { + throw Exception( + "Error getting speech to text: ${result.error}", + ); + } + + _representations = null; + return result.result!; + } + + Future requestSttTranslation({ + required String langCode, + required String l1Code, + required String l2Code, + }) async { + if (!representations.any( + (element) => element.content.speechToText != null, + )) { + await requestSpeechToText(l1Code, l2Code); + } + + final rep = representations.firstWhereOrNull( + (element) => element.content.speechToText != null, + ); + + if (rep == null) { + throw Exception("No speech to text representation found"); + } + + final resp = await rep.requestSttTranslation( + userL1: l1Code, + userL2: l2Code, + ); + return resp.translation; + } + + Future requestRepresentationByDetectedLanguage() async { + LanguageDetectionResponse? resp; + final result = await LanguageDetectionRepo.get( + MatrixState.pangeaController.userController.accessToken, + LanguageDetectionRequest( + text: _latestEdit.body, + senderl1: _l1Code, + senderl2: _l2Code, + ), + ); + + if (result.isError) return null; + resp = result.result!; + + final langCode = resp.detections.firstOrNull?.langCode; + if (langCode == null) return null; + if (langCode == originalSent?.langCode) { + return originalSent?.event?.eventId; + } + + final res = await _requestRepresentation( + originalSent?.content.text ?? _latestEdit.body, + langCode, + originalSent?.langCode ?? LanguageKeys.unknownLanguage, + originalSent: originalSent == null, + ); + + if (res.isError) return null; + return _sendRepresentationEvent(res.result!); + } + + Future requestRespresentationByL1() async { + if (_l1Code == null || _l2Code == null) { + throw Exception("Missing language codes"); + } + + final includedIT = + (originalSent?.choreo?.endedWithIT(originalSent!.text) ?? false) && + !(originalSent?.choreo?.includedIGC ?? true); + + RepresentationEvent? rep; + if (!includedIT) { + // if the message didn't go through translation, get any l1 rep + rep = representationByLanguage(_l1Code!); + } else { + // if the message went through translation, get the non-original + // l1 rep since originalWritten could contain some l2 words + // (https://github.com/pangeachat/client/issues/3591) + rep = representationByLanguage( + _l1Code!, + filter: (rep) => !rep.content.originalWritten, + ); + } + + if (rep != null) return rep.content.text; + + final String srcLang = includedIT + ? (originalWritten?.langCode ?? _l1Code!) + : (originalSent?.langCode ?? _l2Code!); + + final resp = await _requestRepresentation( + includedIT ? originalWrittenContent : messageDisplayText, + _l1Code!, + srcLang, + ); + + if (resp.isError) throw resp.error!; + _sendRepresentationEvent(resp.result!); + return resp.result!.text; + } + + Future> _requestRepresentation( + String text, + String targetLang, + String sourceLang, { + bool originalSent = false, + }) async { + _representations = null; + + final res = await FullTextTranslationRepo.get( + MatrixState.pangeaController.userController.accessToken, + FullTextTranslationRequestModel( + text: text, + srcLang: sourceLang, + tgtLang: targetLang, + userL2: _l2Code ?? LanguageKeys.unknownLanguage, + userL1: _l1Code ?? LanguageKeys.unknownLanguage, + ), + ); + + return res.isError + ? Result.error(res.error!) + : Result.value( + PangeaRepresentation( + langCode: targetLang, + text: res.result!, + originalSent: originalSent, + originalWritten: false, + ), + ); + } + + Future _sendRepresentationEvent( + PangeaRepresentation representation, + ) async { + final repEvent = await room.sendPangeaEvent( + content: representation.toJson(), + parentEventId: eventId, + type: PangeaEventTypes.representation, + ); + return repEvent?.eventId; + } } diff --git a/lib/pangea/events/event_wrappers/pangea_representation_event.dart b/lib/pangea/events/event_wrappers/pangea_representation_event.dart index 06b28c16f..14c4ab0e9 100644 --- a/lib/pangea/events/event_wrappers/pangea_representation_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_representation_event.dart @@ -7,7 +7,6 @@ import 'package:flutter/foundation.dart'; import 'package:async/async.dart'; import 'package:collection/collection.dart'; import 'package:matrix/matrix.dart' hide Result; -import 'package:matrix/src/utils/markdown.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart'; @@ -25,9 +24,6 @@ import 'package:fluffychat/pangea/events/repo/token_api_models.dart'; import 'package:fluffychat/pangea/events/repo/tokens_repo.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; -import 'package:fluffychat/pangea/morphs/morph_features_enum.dart'; -import 'package:fluffychat/pangea/morphs/parts_of_speech_enum.dart'; -import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart'; import 'package:fluffychat/pangea/translation/full_text_translation_repo.dart'; import 'package:fluffychat/pangea/translation/full_text_translation_request_model.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; @@ -62,6 +58,33 @@ class RepresentationEvent { Event? get event => _event; + String get text => content.text; + + String get langCode => content.langCode; + + List? get detections => _tokens?.detections; + + Set get tokenEvents => + _event?.aggregatedEvents( + timeline, + PangeaEventTypes.tokens, + ) ?? + {}; + + Set get sttEvents => + _event?.aggregatedEvents( + timeline, + PangeaEventTypes.sttTranslation, + ) ?? + {}; + + Set get choreoEvents => + _event?.aggregatedEvents( + timeline, + PangeaEventTypes.choreoRecord, + ) ?? + {}; + // Note: in the case where the event is the originalSent or originalWritten event, // the content will be set on initialization by the PangeaMessageEvent // Otherwise, the content will be fetched from the event where it is stored in content[type] @@ -71,50 +94,134 @@ class RepresentationEvent { return _content!; } - String get text => content.text; - - String get langCode => content.langCode; - - bool get botAuthored => - content.originalSent == false && content.originalWritten == false; - - List? get detections => _tokens?.detections; - List? get tokens { if (_tokens != null) return _tokens!.tokens; if (_event == null) return null; - final Set tokenEvents = _event?.aggregatedEvents( - timeline, - PangeaEventTypes.tokens, - ) ?? - {}; - if (tokenEvents.isEmpty) return null; _tokens = tokenEvents.last.getPangeaContent(); return _tokens?.tokens; } - Future>> tokensGlobal( - String senderID, - DateTime timestamp, - ) async { - if (tokens != null) return Result.value(tokens!); + ChoreoRecordModel? get choreo { + if (_choreo != null) return _choreo; - if (_event == null && timestamp.isAfter(DateTime(2024, 9, 25))) { + if (_event == null) { Sentry.addBreadcrumb( Breadcrumb( - message: - 'representation with no _event and no tokens got tokens directly. This means an original_sent with no tokens. This should not happen in messages sent after September 25', - data: { - 'content': content.toJson(), - 'event': _event?.toJson(), - 'timestamp': timestamp.toIso8601String(), - 'senderID': senderID, - }, + message: "_event and _choreo both null", ), ); + return null; } + + if (choreoEvents.isEmpty) return null; + if (choreoEvents.length > 1) { + debugger(when: kDebugMode); + ErrorHandler.logError( + m: 'should not have more than one choreoEvent per representation ${_event?.eventId}', + s: StackTrace.current, + data: {"event": _event?.toJson()}, + ); + } + + return ChoreoEvent(event: choreoEvents.first).content; + } + + List get sttTranslations { + if (content.speechToText == null) return []; + if (_event == null) { + Sentry.addBreadcrumb( + Breadcrumb( + message: "_event and _sttTranslations both null", + ), + ); + return []; + } + + if (sttEvents.isEmpty) return []; + final List sttTranslations = []; + for (final event in sttEvents) { + try { + sttTranslations.add( + SttTranslationModel.fromJson(event.content), + ); + } catch (e) { + Sentry.addBreadcrumb( + Breadcrumb( + message: "Failed to parse STT translation", + data: { + "eventID": event.eventId, + "content": event.content, + "error": e.toString(), + }, + ), + ); + } + } + return sttTranslations; + } + + List get vocabAndMorphUses { + if (tokens == null || tokens!.isEmpty) { + return []; + } + + final metadata = ConstructUseMetaData( + roomId: parentMessageEvent.room.id, + timeStamp: parentMessageEvent.originServerTs, + eventId: parentMessageEvent.eventId, + ); + + return content.vocabAndMorphUses( + tokens: tokens!, + metadata: metadata, + choreo: choreo, + ); + } + + /// Finds the closest non-punctuation token to the given token. + PangeaToken? getClosestNonPunctToken(PangeaToken token) { + // If it's not punctuation, it's already the closest. + if (token.pos != "PUNCT") return token; + + final list = tokens; + if (list == null) return null; + + final index = list.indexOf(token); + if (index == -1) return null; + + PangeaToken? left; + PangeaToken? right; + + // Scan left + for (int i = index - 1; i >= 0; i--) { + if (list[i].pos != "PUNCT") { + left = list[i]; + break; + } + } + + // Scan right + for (int i = index + 1; i < list.length; i++) { + if (list[i].pos != "PUNCT") { + right = list[i]; + break; + } + } + + if (left == null) return right; + if (right == null) return left; + + // Choose the nearest by distance + final leftDistance = token.start - left.end; + final rightDistance = right.start - token.end; + + return leftDistance < rightDistance ? left : right; + } + + Future>> requestTokens() async { + if (tokens != null) return Result.value(tokens!); final res = await TokensRepo.get( MatrixState.pangeaController.userController.accessToken, TokensRequestModel( @@ -140,90 +247,12 @@ class RepresentationEvent { ); } - if (res.isError) { - return Result.error(res.error!); - } else { - return Result.value(res.result!.tokens); - } + return res.isError + ? Result.error(res.error!) + : Result.value(res.result!.tokens); } - Future sendTokensEvent( - String repEventID, - Room room, - String userl1, - String userl2, - ) async { - if (tokens != null) return; - if (_event == null) { - ErrorHandler.logError( - e: "Called getTokensEvent with no _event", - data: {}, - ); - return; - } - - final resp = await TokensRepo.get( - MatrixState.pangeaController.userController.accessToken, - TokensRequestModel( - fullText: text, - langCode: langCode, - senderL1: userl1, - senderL2: userl2, - ), - ); - - if (resp.isError) return; - room.sendPangeaEvent( - content: PangeaMessageTokens( - tokens: resp.result!.tokens, - detections: resp.result!.detections, - ).toJson(), - parentEventId: _event!.eventId, - type: PangeaEventTypes.tokens, - ); - } - - List get sttTranslations { - if (content.speechToText == null) return []; - if (_event == null) { - Sentry.addBreadcrumb( - Breadcrumb( - message: "_event and _sttTranslations both null", - ), - ); - return []; - } - - final Set sttEvents = _event!.aggregatedEvents( - timeline, - PangeaEventTypes.sttTranslation, - ); - - if (sttEvents.isEmpty) return []; - final List sttTranslations = []; - for (final event in sttEvents) { - try { - sttTranslations.add( - SttTranslationModel.fromJson(event.content), - ); - } catch (e) { - Sentry.addBreadcrumb( - Breadcrumb( - message: "Failed to parse STT translation", - data: { - "eventID": event.eventId, - "content": event.content, - "error": e.toString(), - }, - ), - ); - } - } - - return sttTranslations; - } - - Future getSttTranslation({ + Future requestSttTranslation({ required String userL1, required String userL2, }) async { @@ -263,137 +292,4 @@ class RepresentationEvent { return translation; } - - ChoreoRecordModel? get choreo { - if (_choreo != null) return _choreo; - - if (_event == null) { - Sentry.addBreadcrumb( - Breadcrumb( - message: "_event and _choreo both null", - ), - ); - return null; - } - - final Set choreoMatrixEvents = - _event?.aggregatedEvents(timeline, PangeaEventTypes.choreoRecord) ?? {}; - - if (choreoMatrixEvents.isEmpty) return null; - - if (choreoMatrixEvents.length > 1) { - debugger(when: kDebugMode); - ErrorHandler.logError( - m: 'should not have more than one choreoEvent per representation ${_event?.eventId}', - s: StackTrace.current, - data: {"event": _event?.toJson()}, - ); - } - - _choreo = ChoreoEvent(event: choreoMatrixEvents.first).content; - - return _choreo; - } - - String? formatBody() { - return markdown(content.text); - } - - /// Finds the closest non-punctuation token to the given token. - /// - /// This method checks if the provided token is a punctuation token. If it is not, - /// it returns the token itself. If the token is a punctuation token, it searches - /// through the list of tokens to find the closest non-punctuation token either to - /// the left or right of the given token. - /// - /// If both left and right non-punctuation tokens are found, it returns the one - /// that is closest to the given token. If only one of them is found, it returns - /// that token. If no non-punctuation tokens are found, it returns null. - /// - /// - Parameters: - /// - token: The token for which to find the closest non-punctuation token. - /// - /// - Returns: The closest non-punctuation token, or null if no such token exists. - PangeaToken? getClosestNonPunctToken(PangeaToken token) { - if (token.pos != "PUNCT") return token; - if (tokens == null) return null; - final index = tokens!.indexOf(token); - if (index > -1) { - final leftTokens = tokens!.sublist(0, index); - final rightTokens = tokens!.sublist(index + 1); - final leftMostToken = leftTokens.lastWhereOrNull( - (element) => element.pos != "PUNCT", - ); - final rightMostToken = rightTokens.firstWhereOrNull( - (element) => element.pos != "PUNCT", - ); - - if (leftMostToken != null && rightMostToken != null) { - final leftDistance = token.start - leftMostToken.end; - final rightDistance = rightMostToken.start - token.end; - return leftDistance < rightDistance ? leftMostToken : rightMostToken; - } else if (leftMostToken != null) { - return leftMostToken; - } else if (rightMostToken != null) { - return rightMostToken; - } - } - return null; - } - - List get tokensToSave => - tokens?.where((token) => token.lemma.saveVocab).toList() ?? []; - - // List get allTokenMorphsToConstructIdentifiers => tokens?.map((t) => t.morphConstructIds).toList() ?? - // []; - - /// get allTokenMorphsToConstructIdentifiers - Set get morphFeatureSetToPractice => - MorphFeaturesEnum.values.where((feature) { - // pos is always included - if (feature == MorphFeaturesEnum.Pos) { - return true; - } - return tokens?.any((token) => token.morph.containsKey(feature)) ?? - false; - }).toSet(); - - Set posSetToPractice(ActivityTypeEnum a) => - PartOfSpeechEnum.values.where((pos) { - // some pos are not eligible for practice at all - if (!pos.eligibleForPractice(a)) { - return false; - } - return tokens?.any( - (token) => token.pos.toLowerCase() == pos.name.toLowerCase(), - ) ?? - false; - }).toSet(); - - List tagsByFeature(MorphFeaturesEnum feature) { - return tokens - ?.where((t) => t.morph.containsKey(feature)) - .map((t) => t.morph[feature]) - .cast() - .toList() ?? - []; - } - - List vocabAndMorphUses() { - if (tokens == null || tokens!.isEmpty) { - return []; - } - - final metadata = ConstructUseMetaData( - roomId: parentMessageEvent.room.id, - timeStamp: parentMessageEvent.originServerTs, - eventId: parentMessageEvent.eventId, - ); - - return content.vocabAndMorphUses( - tokens: tokens!, - metadata: metadata, - choreo: choreo, - ); - } } diff --git a/lib/pangea/events/repo/language_detection_repo.dart b/lib/pangea/events/repo/language_detection_repo.dart index 97ee1a5e7..c4f6a6864 100644 --- a/lib/pangea/events/repo/language_detection_repo.dart +++ b/lib/pangea/events/repo/language_detection_repo.dart @@ -1,18 +1,47 @@ import 'dart:convert'; +import 'package:async/async.dart'; import 'package:http/http.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; import 'package:fluffychat/pangea/common/network/requests.dart'; import 'package:fluffychat/pangea/common/network/urls.dart'; +import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/events/repo/language_detection_request.dart'; import 'package:fluffychat/pangea/events/repo/language_detection_response.dart'; +class _LanguageDetectionCacheItem { + final Future data; + final DateTime timestamp; + + const _LanguageDetectionCacheItem({ + required this.data, + required this.timestamp, + }); +} + class LanguageDetectionRepo { - static Future get( - String? accessToken, { - required LanguageDetectionRequest request, - }) async { + static final Map _cache = {}; + static const Duration _cacheDuration = Duration(minutes: 10); + + static Future> get( + String accessToken, + LanguageDetectionRequest request, + ) { + final cached = _getCached(request); + if (cached != null) { + return _getResult(request, cached); + } + + final future = _fetch(accessToken, request); + _setCached(request, future); + return _getResult(request, future); + } + + static Future _fetch( + String accessToken, + LanguageDetectionRequest request, + ) async { final Requests req = Requests( accessToken: accessToken, choreoApiKey: Environment.choreoApiKey, @@ -22,12 +51,54 @@ class LanguageDetectionRepo { body: request.toJson(), ); - final Map json = - jsonDecode(utf8.decode(res.bodyBytes).toString()); + if (res.statusCode != 200) { + throw Exception( + 'Failed to detect language: ${res.statusCode} ${res.reasonPhrase}', + ); + } - final LanguageDetectionResponse response = - LanguageDetectionResponse.fromJson(json); - - return response; + return LanguageDetectionResponse.fromJson( + jsonDecode(utf8.decode(res.bodyBytes)), + ); } + + static Future> _getResult( + LanguageDetectionRequest request, + Future future, + ) async { + try { + final res = await future; + return Result.value(res); + } catch (e, s) { + _cache.remove(request.hashCode.toString()); + ErrorHandler.logError( + e: e, + s: s, + data: request.toJson(), + ); + return Result.error(e); + } + } + + static Future? _getCached( + LanguageDetectionRequest request, + ) { + final cacheKeys = [..._cache.keys]; + for (final key in cacheKeys) { + if (DateTime.now().difference(_cache[key]!.timestamp) >= _cacheDuration) { + _cache.remove(key); + } + } + + return _cache[request.hashCode.toString()]?.data; + } + + static void _setCached( + LanguageDetectionRequest request, + Future response, + ) => + _cache[request.hashCode.toString()] = _LanguageDetectionCacheItem( + data: response, + timestamp: DateTime.now(), + ); } diff --git a/lib/pangea/events/repo/language_detection_request.dart b/lib/pangea/events/repo/language_detection_request.dart index eb28d6507..803d5db83 100644 --- a/lib/pangea/events/repo/language_detection_request.dart +++ b/lib/pangea/events/repo/language_detection_request.dart @@ -16,4 +16,16 @@ class LanguageDetectionRequest { 'sender_l2': senderl2, }; } + + @override + int get hashCode => text.hashCode ^ senderl1.hashCode ^ senderl2.hashCode; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + return other is LanguageDetectionRequest && + other.text == text && + other.senderl1 == senderl1 && + other.senderl2 == senderl2; + } } diff --git a/lib/pangea/toolbar/widgets/message_audio_card.dart b/lib/pangea/toolbar/widgets/message_audio_card.dart index dcd48c4bf..626c89e80 100644 --- a/lib/pangea/toolbar/widgets/message_audio_card.dart +++ b/lib/pangea/toolbar/widgets/message_audio_card.dart @@ -10,7 +10,6 @@ import 'package:fluffychat/pages/chat/events/audio_player.dart'; import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.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/events/extensions/pangea_event_extension.dart'; import 'package:fluffychat/pangea/toolbar/controllers/text_to_speech_controller.dart'; class MessageAudioCard extends StatefulWidget { @@ -42,19 +41,9 @@ class MessageAudioCardState extends State { setState(() => _isLoading = true); try { - final String langCode = widget.messageEvent.messageDisplayLangCode; - final Event? localEvent = widget.messageEvent.getTextToSpeechLocal( - langCode, - widget.messageEvent.messageDisplayText, + audioFile = await widget.messageEvent.requestTextToSpeech( + widget.messageEvent.messageDisplayLangCode, ); - - if (localEvent != null) { - audioFile = await localEvent.getPangeaAudioFile(); - } else { - audioFile = await widget.messageEvent.getMatrixAudioFile( - langCode, - ); - } debugPrint("audio file is now: $audioFile. setting starts and ends..."); if (mounted) setState(() => _isLoading = false); } catch (e, s) { diff --git a/lib/pangea/toolbar/widgets/message_selection_overlay.dart b/lib/pangea/toolbar/widgets/message_selection_overlay.dart index 9753d9718..c631e858c 100644 --- a/lib/pangea/toolbar/widgets/message_selection_overlay.dart +++ b/lib/pangea/toolbar/widgets/message_selection_overlay.dart @@ -112,12 +112,7 @@ class MessageOverlayController extends State } if (repEvent?.event != null) { - await repEvent!.sendTokensEvent( - repEvent.event!.eventId, - widget._event.room, - MatrixState.pangeaController.languageController.userL1!.langCode, - MatrixState.pangeaController.languageController.userL2!.langCode, - ); + await repEvent!.requestTokens(); } } catch (e, s) { debugger(when: kDebugMode); @@ -268,7 +263,8 @@ class MessageOverlayController extends State pangeaMessageEvent.messageDisplayRepresentation; if (repEvent != null) return repEvent; - final eventID = await pangeaMessageEvent.representationByDetectedLanguage(); + final eventID = + await pangeaMessageEvent.requestRepresentationByDetectedLanguage(); if (eventID == null) return null; final event = await widget._event.room.getEventById(eventID); diff --git a/lib/pangea/toolbar/widgets/select_mode_controller.dart b/lib/pangea/toolbar/widgets/select_mode_controller.dart index fd7a760ae..cfe98d4c5 100644 --- a/lib/pangea/toolbar/widgets/select_mode_controller.dart +++ b/lib/pangea/toolbar/widgets/select_mode_controller.dart @@ -8,7 +8,6 @@ import 'package:path_provider/path_provider.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/extensions/pangea_event_extension.dart'; import 'package:fluffychat/pangea/speech_to_text/speech_to_text_response_model.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_audio_card.dart'; import 'package:fluffychat/pangea/toolbar/widgets/select_mode_buttons.dart'; @@ -19,7 +18,7 @@ class _TranscriptionLoader extends AsyncLoader { _TranscriptionLoader(this.messageEvent) : super(); @override - Future fetch() => messageEvent.getSpeechToText( + Future fetch() => messageEvent.requestSpeechToText( MatrixState.pangeaController.languageController.userL1!.langCodeShort, MatrixState.pangeaController.languageController.userL2!.langCodeShort, ); @@ -30,7 +29,7 @@ class _STTTranslationLoader extends AsyncLoader { _STTTranslationLoader(this.messageEvent) : super(); @override - Future fetch() => messageEvent.sttTranslationByLanguageGlobal( + Future fetch() => messageEvent.requestSttTranslation( langCode: MatrixState .pangeaController.languageController.userL1!.langCodeShort, l1Code: MatrixState @@ -45,7 +44,7 @@ class _TranslationLoader extends AsyncLoader { _TranslationLoader(this.messageEvent) : super(); @override - Future fetch() => messageEvent.l1Respresentation(); + Future fetch() => messageEvent.requestRespresentationByL1(); } class _AudioLoader extends AsyncLoader<(PangeaAudioFile, File?)> { @@ -54,25 +53,10 @@ class _AudioLoader extends AsyncLoader<(PangeaAudioFile, File?)> { @override Future<(PangeaAudioFile, File?)> fetch() async { - final String langCode = messageEvent.messageDisplayLangCode; - - final Event? localEvent = messageEvent.getTextToSpeechLocal( - langCode, - messageEvent.messageDisplayText, + final audioBytes = await messageEvent.requestTextToSpeech( + messageEvent.messageDisplayLangCode, ); - PangeaAudioFile? audioBytes; - if (localEvent != null) { - audioBytes = await localEvent.getPangeaAudioFile(); - } else { - audioBytes = await messageEvent.getMatrixAudioFile( - langCode, - ); - } - if (audioBytes == null) { - throw Exception('Audio bytes are null'); - } - File? audioFile; if (!kIsWeb) { final tempDir = await getTemporaryDirectory(); From e184e9a76f4ed159273b52b31a9ef0f4365ff945 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 3 Dec 2025 15:39:29 -0500 Subject: [PATCH 4/9] move text to speech controller out of pangea controller --- lib/pages/chat/events/message_content.dart | 2 +- lib/pangea/choreographer/choreographer.dart | 2 +- .../common/controllers/pangea_controller.dart | 5 +- lib/pangea/common/widgets/choice_array.dart | 2 +- .../event_wrappers/pangea_message_event.dart | 16 +- .../extensions/pangea_event_extension.dart | 2 +- .../pages/settings_learning.dart | 2 +- .../phonetic_transcription_widget.dart | 2 +- .../text_to_speech/text_to_speech_repo.dart | 105 ++++++++ .../text_to_speech_request_model.dart | 38 +++ .../text_to_speech_response_model.dart | 117 +++++++++ .../tts_controller.dart | 52 ++-- .../text_to_speech_controller.dart | 243 ------------------ .../practice_match_item.dart | 2 +- .../toolbar/widgets/message_audio_card.dart | 2 +- .../widgets/message_selection_overlay.dart | 2 +- .../practice_activity/word_audio_button.dart | 2 +- .../toolbar/widgets/select_mode_buttons.dart | 2 +- 18 files changed, 308 insertions(+), 290 deletions(-) create mode 100644 lib/pangea/text_to_speech/text_to_speech_repo.dart create mode 100644 lib/pangea/text_to_speech/text_to_speech_request_model.dart create mode 100644 lib/pangea/text_to_speech/text_to_speech_response_model.dart rename lib/pangea/{toolbar/controllers => text_to_speech}/tts_controller.dart (90%) delete mode 100644 lib/pangea/toolbar/controllers/text_to_speech_controller.dart diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 9f31ec73d..7aa5794e4 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -10,7 +10,7 @@ import 'package:fluffychat/pages/chat/events/video_player.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.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/toolbar/controllers/tts_controller.dart'; +import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; import 'package:fluffychat/pangea/toolbar/enums/reading_assistance_mode_enum.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart'; import 'package:fluffychat/utils/event_checkbox_extension.dart'; diff --git a/lib/pangea/choreographer/choreographer.dart b/lib/pangea/choreographer/choreographer.dart index 8c936a3d7..c0a63bca2 100644 --- a/lib/pangea/choreographer/choreographer.dart +++ b/lib/pangea/choreographer/choreographer.dart @@ -21,7 +21,7 @@ import 'package:fluffychat/pangea/events/repo/tokens_repo.dart'; import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; import 'package:fluffychat/pangea/spaces/models/space_model.dart'; import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart'; -import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart'; +import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import '../../widgets/matrix.dart'; import 'choreographer_error_controller.dart'; diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index 2595b1819..9208816af 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -22,8 +22,7 @@ import 'package:fluffychat/pangea/learning_settings/utils/locale_provider.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; import 'package:fluffychat/pangea/spaces/controllers/space_code_controller.dart'; import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart'; -import 'package:fluffychat/pangea/toolbar/controllers/text_to_speech_controller.dart'; -import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart'; +import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; import 'package:fluffychat/pangea/user/controllers/permissions_controller.dart'; import 'package:fluffychat/pangea/user/controllers/user_controller.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -38,7 +37,6 @@ class PangeaController { late GetAnalyticsController getAnalytics; late PutAnalyticsController putAnalytics; late SubscriptionController subscriptionController; - late TextToSpeechController textToSpeech; ///store Services final pLanguageStore = PLanguageStore(); @@ -82,7 +80,6 @@ class PangeaController { getAnalytics = GetAnalyticsController(this); putAnalytics = PutAnalyticsController(this); subscriptionController = SubscriptionController(this); - textToSpeech = TextToSpeechController(this); PAuthGaurd.pController = this; } diff --git a/lib/pangea/common/widgets/choice_array.dart b/lib/pangea/common/widgets/choice_array.dart index 3a953f0e6..d0c109259 100644 --- a/lib/pangea/common/widgets/choice_array.dart +++ b/lib/pangea/common/widgets/choice_array.dart @@ -5,7 +5,7 @@ import 'package:collection/collection.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/widgets/choice_animation.dart'; -import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart'; +import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; import 'package:fluffychat/widgets/matrix.dart'; import '../../bot/utils/bot_style.dart'; import '../../choreographer/it/it_shimmer.dart'; diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index 230974851..621986924 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -24,7 +24,9 @@ import 'package:fluffychat/pangea/speech_to_text/audio_encoding_enum.dart'; import 'package:fluffychat/pangea/speech_to_text/speech_to_text_repo.dart'; import 'package:fluffychat/pangea/speech_to_text/speech_to_text_request_model.dart'; import 'package:fluffychat/pangea/speech_to_text/speech_to_text_response_model.dart'; -import 'package:fluffychat/pangea/toolbar/controllers/text_to_speech_controller.dart'; +import 'package:fluffychat/pangea/text_to_speech/text_to_speech_repo.dart'; +import 'package:fluffychat/pangea/text_to_speech/text_to_speech_request_model.dart'; +import 'package:fluffychat/pangea/text_to_speech/text_to_speech_response_model.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_audio_card.dart'; import 'package:fluffychat/pangea/translation/full_text_translation_repo.dart'; import 'package:fluffychat/pangea/translation/full_text_translation_request_model.dart'; @@ -360,7 +362,7 @@ class PangeaMessageEvent { final rep = representationByLanguage(langCode); final tokensResp = await rep?.requestTokens(); - final request = TextToSpeechRequest( + final request = TextToSpeechRequestModel( text: rep?.content.text ?? body, tokens: tokensResp?.result?.map((t) => t.text).toList() ?? [], langCode: langCode, @@ -368,10 +370,18 @@ class PangeaMessageEvent { userL2: _l2Code ?? LanguageKeys.unknownLanguage, ); - final response = await MatrixState.pangeaController.textToSpeech.get( + final result = await TextToSpeechRepo.get( + MatrixState.pangeaController.userController.accessToken, request, ); + if (result.error != null) { + throw Exception( + "Error getting text to speech: ${result.error}", + ); + } + + final response = result.result!; final audioBytes = base64.decode(response.audioContent); final fileName = "audio_for_${_event.eventId}_$langCode.${response.fileExtension}"; diff --git a/lib/pangea/events/extensions/pangea_event_extension.dart b/lib/pangea/events/extensions/pangea_event_extension.dart index 77014bfcf..8d18c5ec5 100644 --- a/lib/pangea/events/extensions/pangea_event_extension.dart +++ b/lib/pangea/events/extensions/pangea_event_extension.dart @@ -11,7 +11,7 @@ import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/events/models/representation_content_model.dart'; import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart'; -import 'package:fluffychat/pangea/toolbar/controllers/text_to_speech_controller.dart'; +import 'package:fluffychat/pangea/text_to_speech/text_to_speech_response_model.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_audio_card.dart'; extension PangeaEvent on Event { diff --git a/lib/pangea/learning_settings/pages/settings_learning.dart b/lib/pangea/learning_settings/pages/settings_learning.dart index c57c66229..e894bca95 100644 --- a/lib/pangea/learning_settings/pages/settings_learning.dart +++ b/lib/pangea/learning_settings/pages/settings_learning.dart @@ -14,7 +14,7 @@ import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; import 'package:fluffychat/pangea/learning_settings/pages/settings_learning_view.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; import 'package:fluffychat/pangea/spaces/models/space_model.dart'; -import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart'; +import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; import 'package:fluffychat/pangea/user/models/user_model.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; diff --git a/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart b/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart index a610cc28b..df866e653 100644 --- a/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart +++ b/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart @@ -10,7 +10,7 @@ import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_repo.dart'; import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_request.dart'; -import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart'; +import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; import 'package:fluffychat/widgets/hover_builder.dart'; import 'package:fluffychat/widgets/matrix.dart'; diff --git a/lib/pangea/text_to_speech/text_to_speech_repo.dart b/lib/pangea/text_to_speech/text_to_speech_repo.dart new file mode 100644 index 000000000..3b68486e6 --- /dev/null +++ b/lib/pangea/text_to_speech/text_to_speech_repo.dart @@ -0,0 +1,105 @@ +import 'dart:convert'; + +import 'package:async/async.dart'; +import 'package:http/http.dart'; + +import 'package:fluffychat/pangea/common/config/environment.dart'; +import 'package:fluffychat/pangea/common/network/requests.dart'; +import 'package:fluffychat/pangea/common/network/urls.dart'; +import 'package:fluffychat/pangea/common/utils/error_handler.dart'; +import 'package:fluffychat/pangea/text_to_speech/text_to_speech_request_model.dart'; +import 'package:fluffychat/pangea/text_to_speech/text_to_speech_response_model.dart'; + +class _TextToSpeechCacheItem { + final Future data; + final DateTime timestamp; + + const _TextToSpeechCacheItem({ + required this.data, + required this.timestamp, + }); +} + +class TextToSpeechRepo { + static final Map _cache = {}; + static const Duration _cacheDuration = Duration(minutes: 10); + + static Future> get( + String accessToken, + TextToSpeechRequestModel request, + ) { + final cached = _getCached(request); + if (cached != null) { + return _getResult(request, cached); + } + + final future = _fetch(accessToken, request); + _setCached(request, future); + return _getResult(request, future); + } + + static Future _fetch( + String accessToken, + TextToSpeechRequestModel request, + ) async { + final Requests req = Requests( + choreoApiKey: Environment.choreoApiKey, + accessToken: accessToken, + ); + + final Response res = await req.post( + url: PApiUrls.textToSpeech, + body: request.toJson(), + ); + + if (res.statusCode != 200) { + throw Exception( + 'Failed to convert text to speech: ${res.statusCode} ${res.reasonPhrase}', + ); + } + + return TextToSpeechResponseModel.fromJson( + jsonDecode(utf8.decode(res.bodyBytes)), + ); + } + + static Future> _getResult( + TextToSpeechRequestModel request, + Future future, + ) async { + try { + final res = await future; + return Result.value(res); + } catch (e, s) { + _cache.remove(request.hashCode.toString()); + ErrorHandler.logError( + e: e, + s: s, + data: request.toJson(), + ); + return Result.error(e); + } + } + + static Future? _getCached( + TextToSpeechRequestModel request, + ) { + final cacheKeys = [..._cache.keys]; + for (final key in cacheKeys) { + if (DateTime.now().difference(_cache[key]!.timestamp) >= _cacheDuration) { + _cache.remove(key); + } + } + + return _cache[request.hashCode.toString()]?.data; + } + + static void _setCached( + TextToSpeechRequestModel request, + Future response, + ) => + _cache[request.hashCode.toString()] = _TextToSpeechCacheItem( + data: response, + timestamp: DateTime.now(), + ); +} diff --git a/lib/pangea/text_to_speech/text_to_speech_request_model.dart b/lib/pangea/text_to_speech/text_to_speech_request_model.dart new file mode 100644 index 000000000..9c18104f7 --- /dev/null +++ b/lib/pangea/text_to_speech/text_to_speech_request_model.dart @@ -0,0 +1,38 @@ +import 'package:fluffychat/pangea/common/constants/model_keys.dart'; +import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; + +class TextToSpeechRequestModel { + String text; + String langCode; + String userL1; + String userL2; + List tokens; + + TextToSpeechRequestModel({ + required this.text, + required this.langCode, + required this.userL1, + required this.userL2, + required this.tokens, + }); + + Map toJson() => { + ModelKey.text: text, + ModelKey.langCode: langCode, + ModelKey.userL1: userL1, + ModelKey.userL2: userL2, + ModelKey.tokens: tokens.map((token) => token.toJson()).toList(), + }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is TextToSpeechRequestModel && + other.text == text && + other.langCode == langCode; + } + + @override + int get hashCode => text.hashCode ^ langCode.hashCode; +} 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 new file mode 100644 index 000000000..24cc71e1a --- /dev/null +++ b/lib/pangea/text_to_speech/text_to_speech_response_model.dart @@ -0,0 +1,117 @@ +import 'package:fluffychat/pangea/common/constants/model_keys.dart'; +import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; + +class TextToSpeechResponseModel { + String audioContent; + String mimeType; + int durationMillis; + List waveform; + String fileExtension; + List ttsTokens; + + TextToSpeechResponseModel({ + required this.audioContent, + required this.mimeType, + required this.durationMillis, + required this.waveform, + required this.fileExtension, + required this.ttsTokens, + }); + + factory TextToSpeechResponseModel.fromJson( + Map json, + ) => + TextToSpeechResponseModel( + audioContent: json["audio_content"], + mimeType: json["mime_type"], + durationMillis: json["duration_millis"], + waveform: List.from(json["wave_form"]), + fileExtension: json["file_extension"], + ttsTokens: List.from( + json["tts_tokens"].map((x) => TTSToken.fromJson(x)), + ), + ); + + Map toJson() => { + "audio_content": audioContent, + "mime_type": mimeType, + "duration_millis": durationMillis, + "wave_form": List.from(waveform.map((x) => x)), + "file_extension": fileExtension, + "tts_tokens": List.from(ttsTokens.map((x) => x.toJson())), + }; + + PangeaAudioEventData toPangeaAudioEventData(String text, String langCode) { + return PangeaAudioEventData( + text: text, + langCode: langCode, + tokens: ttsTokens, + ); + } +} + +class TTSToken { + final int startMS; + final int endMS; + final PangeaTokenText text; + + TTSToken({ + required this.startMS, + required this.endMS, + required this.text, + }); + + factory TTSToken.fromJson(Map json) => TTSToken( + startMS: json["start_ms"], + endMS: json["end_ms"], + text: PangeaTokenText.fromJson(json["text"]), + ); + + Map toJson() => { + "start_ms": startMS, + "end_ms": endMS, + "text": text.toJson(), + }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is TTSToken && + other.startMS == startMS && + other.endMS == endMS && + other.text == text; + } + + @override + int get hashCode => startMS.hashCode ^ endMS.hashCode ^ text.hashCode; +} + +class PangeaAudioEventData { + final String text; + final String langCode; + final List tokens; + + PangeaAudioEventData({ + required this.text, + required this.langCode, + required this.tokens, + }); + + factory PangeaAudioEventData.fromJson(dynamic json) => PangeaAudioEventData( + text: json[ModelKey.text] as String, + langCode: json[ModelKey.langCode] as String, + tokens: List.from( + (json[ModelKey.tokens] as Iterable) + .map((x) => TTSToken.fromJson(x)) + .toList(), + ), + ); + + Map toJson() => { + ModelKey.text: text, + ModelKey.langCode: langCode, + ModelKey.tokens: + List>.from(tokens.map((x) => x.toJson())), + }; +} diff --git a/lib/pangea/toolbar/controllers/tts_controller.dart b/lib/pangea/text_to_speech/tts_controller.dart similarity index 90% rename from lib/pangea/toolbar/controllers/tts_controller.dart rename to lib/pangea/text_to_speech/tts_controller.dart index a7f90f52a..6ca6745f5 100644 --- a/lib/pangea/toolbar/controllers/tts_controller.dart +++ b/lib/pangea/text_to_speech/tts_controller.dart @@ -20,7 +20,10 @@ import 'package:fluffychat/pangea/common/widgets/card_header.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; import 'package:fluffychat/pangea/instructions/instructions_enum.dart'; import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; -import 'package:fluffychat/pangea/toolbar/controllers/text_to_speech_controller.dart'; +import 'package:fluffychat/pangea/text_to_speech/text_to_speech_repo.dart'; +import 'package:fluffychat/pangea/text_to_speech/text_to_speech_request_model.dart'; +import 'package:fluffychat/pangea/text_to_speech/text_to_speech_response_model.dart'; +import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart' @@ -263,35 +266,26 @@ class TtsController { String langCode, List tokens, ) async { - TextToSpeechResponse? ttsRes; - try { - loadingChoreoStream.add(true); - ttsRes = await MatrixState.pangeaController.textToSpeech.get( - TextToSpeechRequest( - text: text, - langCode: langCode, - tokens: tokens, - userL1: - MatrixState.pangeaController.languageController.activeL1Code() ?? - LanguageKeys.unknownLanguage, - userL2: - MatrixState.pangeaController.languageController.activeL2Code() ?? - LanguageKeys.unknownLanguage, - ), - ); - } catch (e, s) { - error_handler.ErrorHandler.logError( - e: e, - s: s, - data: { - 'text': text, - }, - ); - } finally { - loadingChoreoStream.add(false); - } + TextToSpeechResponseModel? ttsRes; - if (ttsRes == null) return; + loadingChoreoStream.add(true); + final result = await TextToSpeechRepo.get( + MatrixState.pangeaController.userController.accessToken, + TextToSpeechRequestModel( + text: text, + langCode: langCode, + tokens: tokens, + userL1: + MatrixState.pangeaController.languageController.activeL1Code() ?? + LanguageKeys.unknownLanguage, + userL2: + MatrixState.pangeaController.languageController.activeL2Code() ?? + LanguageKeys.unknownLanguage, + ), + ); + loadingChoreoStream.add(false); + if (result.isError) return; + ttsRes = result.result!; try { Logs().i('Speaking from choreo: $text, langCode: $langCode'); diff --git a/lib/pangea/toolbar/controllers/text_to_speech_controller.dart b/lib/pangea/toolbar/controllers/text_to_speech_controller.dart deleted file mode 100644 index 54a9768ca..000000000 --- a/lib/pangea/toolbar/controllers/text_to_speech_controller.dart +++ /dev/null @@ -1,243 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:http/http.dart'; - -import 'package:fluffychat/pangea/common/config/environment.dart'; -import 'package:fluffychat/pangea/common/constants/model_keys.dart'; -import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; -import 'package:fluffychat/pangea/common/network/requests.dart'; -import 'package:fluffychat/pangea/common/network/urls.dart'; -import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; - -class PangeaAudioEventData { - final String text; - final String langCode; - final List tokens; - - PangeaAudioEventData({ - required this.text, - required this.langCode, - required this.tokens, - }); - - factory PangeaAudioEventData.fromJson(dynamic json) => PangeaAudioEventData( - text: json[ModelKey.text] as String, - langCode: json[ModelKey.langCode] as String, - tokens: List.from( - (json[ModelKey.tokens] as Iterable) - .map((x) => TTSToken.fromJson(x)) - .toList(), - ), - ); - - Map toJson() => { - ModelKey.text: text, - ModelKey.langCode: langCode, - ModelKey.tokens: - List>.from(tokens.map((x) => x.toJson())), - }; -} - -class TTSToken { - final int startMS; - final int endMS; - final PangeaTokenText text; - - TTSToken({ - required this.startMS, - required this.endMS, - required this.text, - }); - - factory TTSToken.fromJson(Map json) => TTSToken( - startMS: json["start_ms"], - endMS: json["end_ms"], - text: PangeaTokenText.fromJson(json["text"]), - ); - - Map toJson() => { - "start_ms": startMS, - "end_ms": endMS, - "text": text.toJson(), - }; - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is TTSToken && - other.startMS == startMS && - other.endMS == endMS && - other.text == text; - } - - @override - int get hashCode => startMS.hashCode ^ endMS.hashCode ^ text.hashCode; -} - -class TextToSpeechRequest { - String text; - String langCode; - String userL1; - String userL2; - List tokens; - - TextToSpeechRequest({ - required this.text, - required this.langCode, - required this.userL1, - required this.userL2, - required this.tokens, - }); - - Map toJson() => { - ModelKey.text: text, - ModelKey.langCode: langCode, - ModelKey.userL1: userL1, - ModelKey.userL2: userL2, - ModelKey.tokens: tokens.map((token) => token.toJson()).toList(), - }; - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is TextToSpeechRequest && - other.text == text && - other.langCode == langCode; - } - - @override - int get hashCode => text.hashCode ^ langCode.hashCode; -} - -class TextToSpeechResponse { - String audioContent; - String mimeType; - int durationMillis; - List waveform; - String fileExtension; - List ttsTokens; - - TextToSpeechResponse({ - required this.audioContent, - required this.mimeType, - required this.durationMillis, - required this.waveform, - required this.fileExtension, - required this.ttsTokens, - }); - - factory TextToSpeechResponse.fromJson( - Map json, - ) => - TextToSpeechResponse( - audioContent: json["audio_content"], - mimeType: json["mime_type"], - durationMillis: json["duration_millis"], - waveform: List.from(json["wave_form"]), - fileExtension: json["file_extension"], - ttsTokens: List.from( - json["tts_tokens"].map((x) => TTSToken.fromJson(x)), - ), - ); - - Map toJson() => { - "audio_content": audioContent, - "mime_type": mimeType, - "duration_millis": durationMillis, - "wave_form": List.from(waveform.map((x) => x)), - "file_extension": fileExtension, - "tts_tokens": List.from(ttsTokens.map((x) => x.toJson())), - }; - - PangeaAudioEventData toPangeaAudioEventData(String text, String langCode) { - return PangeaAudioEventData( - text: text, - langCode: langCode, - tokens: ttsTokens, - ); - } -} - -class _TextToSpeechCacheItem { - Future data; - - _TextToSpeechCacheItem({ - required this.data, - }); -} - -class TextToSpeechController { - static final Map _cache = {}; - late final PangeaController _pangeaController; - - Timer? _cacheClearTimer; - - TextToSpeechController(PangeaController pangeaController) { - _pangeaController = pangeaController; - _initializeCacheClearing(); - } - - void _initializeCacheClearing() { - const duration = Duration(minutes: 15); // Adjust the duration as needed - _cacheClearTimer = Timer.periodic(duration, (Timer t) => _clearCache()); - } - - void _clearCache() { - _cache.clear(); - } - - void dispose() { - _cacheClearTimer?.cancel(); - } - - Future get( - TextToSpeechRequest params, - ) async { - if (_cache.containsKey(params)) { - return _cache[params]!.data; - } else { - final Future response = _fetchResponse( - _pangeaController.userController.accessToken, - params, - ); - _cache[params] = _TextToSpeechCacheItem(data: response); - return response; - } - } - - static Future _fetchResponse( - String accessToken, - TextToSpeechRequest params, - ) async { - final Requests request = Requests( - choreoApiKey: Environment.choreoApiKey, - accessToken: accessToken, - ); - - final Response res = await request.post( - url: PApiUrls.textToSpeech, - body: params.toJson(), - ); - - final Map json = jsonDecode(res.body); - - return TextToSpeechResponse.fromJson(json); - } - - static bool isOggFile(Uint8List bytes) { - // Check if the file has enough bytes for the header - if (bytes.length < 4) { - return false; - } - - // Check the magic number for OGG file - return bytes[0] == 0x4F && - bytes[1] == 0x67 && - bytes[2] == 0x67 && - bytes[3] == 0x53; - } -} diff --git a/lib/pangea/toolbar/reading_assistance_input_row/practice_match_item.dart b/lib/pangea/toolbar/reading_assistance_input_row/practice_match_item.dart index 5ff61829e..87d0e8adc 100644 --- a/lib/pangea/toolbar/reading_assistance_input_row/practice_match_item.dart +++ b/lib/pangea/toolbar/reading_assistance_input_row/practice_match_item.dart @@ -7,7 +7,7 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/practice_activities/practice_choice.dart'; -import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart'; +import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; import 'package:fluffychat/pangea/toolbar/widgets/practice_controller.dart'; import 'package:fluffychat/widgets/matrix.dart'; diff --git a/lib/pangea/toolbar/widgets/message_audio_card.dart b/lib/pangea/toolbar/widgets/message_audio_card.dart index 626c89e80..82d2624c8 100644 --- a/lib/pangea/toolbar/widgets/message_audio_card.dart +++ b/lib/pangea/toolbar/widgets/message_audio_card.dart @@ -10,7 +10,7 @@ import 'package:fluffychat/pages/chat/events/audio_player.dart'; import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.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/toolbar/controllers/text_to_speech_controller.dart'; +import 'package:fluffychat/pangea/text_to_speech/text_to_speech_response_model.dart'; class MessageAudioCard extends StatefulWidget { final PangeaMessageEvent messageEvent; diff --git a/lib/pangea/toolbar/widgets/message_selection_overlay.dart b/lib/pangea/toolbar/widgets/message_selection_overlay.dart index c631e858c..148e1814c 100644 --- a/lib/pangea/toolbar/widgets/message_selection_overlay.dart +++ b/lib/pangea/toolbar/widgets/message_selection_overlay.dart @@ -20,7 +20,7 @@ import 'package:fluffychat/pangea/events/event_wrappers/pangea_representation_ev import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; import 'package:fluffychat/pangea/message_token_text/tokens_util.dart'; -import 'package:fluffychat/pangea/toolbar/controllers/text_to_speech_controller.dart'; +import 'package:fluffychat/pangea/text_to_speech/text_to_speech_response_model.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_selection_positioner.dart'; import 'package:fluffychat/pangea/toolbar/widgets/practice_controller.dart'; import 'package:fluffychat/pangea/toolbar/widgets/select_mode_buttons.dart'; diff --git a/lib/pangea/toolbar/widgets/practice_activity/word_audio_button.dart b/lib/pangea/toolbar/widgets/practice_activity/word_audio_button.dart index cac792e8c..385290e38 100644 --- a/lib/pangea/toolbar/widgets/practice_activity/word_audio_button.dart +++ b/lib/pangea/toolbar/widgets/practice_activity/word_audio_button.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart'; +import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; import 'package:fluffychat/widgets/matrix.dart'; class WordAudioButton extends StatefulWidget { diff --git a/lib/pangea/toolbar/widgets/select_mode_buttons.dart b/lib/pangea/toolbar/widgets/select_mode_buttons.dart index 5610ecb7b..c824d7236 100644 --- a/lib/pangea/toolbar/widgets/select_mode_buttons.dart +++ b/lib/pangea/toolbar/widgets/select_mode_buttons.dart @@ -17,7 +17,7 @@ import 'package:fluffychat/pangea/common/widgets/pressable_button.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart'; import 'package:fluffychat/pangea/events/utils/report_message.dart'; -import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart'; +import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_audio_card.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart'; import 'package:fluffychat/pangea/toolbar/widgets/select_mode_controller.dart'; From 97bfdb26033bdf287c9b501b781b71e1936a0e28 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 3 Dec 2025 16:36:41 -0500 Subject: [PATCH 5/9] remove space code controller from pangea controller --- lib/config/routes.dart | 4 +- lib/main.dart | 1 + lib/pages/chat/chat.dart | 2 +- lib/pages/chat_list/chat_list.dart | 22 +- .../activity_participant_list.dart | 2 +- .../utils/chat_list_handle_space_tap.dart | 4 +- .../pangea_invitation_selection_view.dart | 2 +- .../pages/room_participants_widget.dart | 2 +- lib/pangea/choreographer/choreographer.dart | 2 +- .../common/controllers/pangea_controller.dart | 38 --- .../common/widgets/share_room_button.dart | 2 +- .../course_chats/course_chats_page.dart | 2 +- .../course_default_chats_enum.dart | 2 +- .../courses/course_plan_room_extension.dart | 2 +- .../topic_participant_list.dart | 2 +- .../event_wrappers/pangea_message_event.dart | 2 +- .../extensions/join_rule_extension.dart | 6 +- .../extensions/pangea_room_extension.dart | 2 +- .../pangea_rooms_chunk_extension.dart | 2 +- .../enums/tool_settings_enum.dart | 70 ++++ .../pages/settings_learning.dart | 2 +- .../pages/settings_learning_view.dart | 2 +- lib/pangea/login/pages/course_code_page.dart | 4 +- .../pages/create_pangea_account_page.dart | 10 +- .../login/pages/login_or_signup_view.dart | 5 +- .../public_room_bottom_sheet.dart | 4 +- .../download_space_analytics_dialog.dart | 0 .../space_analytics/space_analytics_view.dart | 2 +- lib/pangea/spaces/models/space_model.dart | 299 ------------------ .../space_code_controller.dart | 80 ++--- lib/pangea/spaces/space_code_repo.dart | 36 +++ .../{constants => }/space_constants.dart | 3 - lib/pangea/spaces/utils/space_code.dart | 26 -- .../spaces/utils/space_code_extension.dart | 34 ++ .../join_with_link_page.dart} | 5 +- .../widgets/leaderboard_participant_list.dart | 153 --------- .../load_participants_builder.dart} | 0 .../controllers/permissions_controller.dart | 2 +- lib/pangea/user/models/user_model.dart | 2 +- lib/widgets/matrix.dart | 2 +- 40 files changed, 211 insertions(+), 631 deletions(-) create mode 100644 lib/pangea/learning_settings/enums/tool_settings_enum.dart rename lib/pangea/{spaces/widgets => space_analytics}/download_space_analytics_dialog.dart (100%) delete mode 100644 lib/pangea/spaces/models/space_model.dart rename lib/pangea/spaces/{controllers => }/space_code_controller.dart (67%) create mode 100644 lib/pangea/spaces/space_code_repo.dart rename lib/pangea/spaces/{constants => }/space_constants.dart (88%) delete mode 100644 lib/pangea/spaces/utils/space_code.dart create mode 100644 lib/pangea/spaces/utils/space_code_extension.dart rename lib/pangea/spaces/{utils/join_with_link.dart => widgets/join_with_link_page.dart} (88%) delete mode 100644 lib/pangea/spaces/widgets/leaderboard_participant_list.dart rename lib/pangea/spaces/{utils/load_participants_util.dart => widgets/load_participants_builder.dart} (100%) diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 58da1b0cb..4b1fe560a 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -53,8 +53,8 @@ import 'package:fluffychat/pangea/login/pages/new_course_page.dart'; import 'package:fluffychat/pangea/login/pages/public_courses_page.dart'; import 'package:fluffychat/pangea/login/pages/signup.dart'; import 'package:fluffychat/pangea/space_analytics/space_analytics.dart'; -import 'package:fluffychat/pangea/spaces/constants/space_constants.dart'; -import 'package:fluffychat/pangea/spaces/utils/join_with_link.dart'; +import 'package:fluffychat/pangea/spaces/space_constants.dart'; +import 'package:fluffychat/pangea/spaces/widgets/join_with_link_page.dart'; import 'package:fluffychat/pangea/subscription/pages/settings_subscription.dart'; import 'package:fluffychat/widgets/config_viewer.dart'; import 'package:fluffychat/widgets/layouts/empty_page.dart'; diff --git a/lib/main.dart b/lib/main.dart index 87b78c2cd..6326a8b06 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -45,6 +45,7 @@ void main() async { final List initFutures = [ GetStorage.init(), GetStorage.init("subscription_storage"), + GetStorage.init('class_storage'), ]; await Future.wait(initFutures); // Pangea# diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 199392716..ef53d7696 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -62,7 +62,7 @@ import 'package:fluffychat/pangea/learning_settings/constants/language_constants import 'package:fluffychat/pangea/learning_settings/repo/language_mismatch_repo.dart'; import 'package:fluffychat/pangea/learning_settings/widgets/p_language_dialog.dart'; import 'package:fluffychat/pangea/message_token_text/tokens_util.dart'; -import 'package:fluffychat/pangea/spaces/utils/load_participants_util.dart'; +import 'package:fluffychat/pangea/spaces/widgets/load_participants_builder.dart'; import 'package:fluffychat/pangea/subscription/widgets/paywall_card.dart'; import 'package:fluffychat/pangea/token_info_feedback/token_info_feedback_dialog.dart'; import 'package:fluffychat/pangea/token_info_feedback/token_info_feedback_notification.dart'; diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index 1419dc938..90d3bcfa1 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -20,6 +20,8 @@ import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart import 'package:fluffychat/pangea/chat_settings/widgets/chat_context_menu_action.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; +import 'package:fluffychat/pangea/spaces/space_code_controller.dart'; +import 'package:fluffychat/pangea/spaces/space_code_repo.dart'; import 'package:fluffychat/pangea/subscription/widgets/subscription_snackbar.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; @@ -40,6 +42,7 @@ import '../../widgets/matrix.dart'; import 'package:fluffychat/utils/tor_stub.dart' if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart'; + enum PopupMenuAction { settings, invite, @@ -587,10 +590,8 @@ class ChatListController extends State spaceId, ); - final String? justInputtedCode = - MatrixState.pangeaController.spaceCodeController.justInputtedCode; - final newSpaceCode = space?.classCode; - if (newSpaceCode?.toLowerCase() == justInputtedCode?.toLowerCase()) { + if (space?.classCode?.toLowerCase() == + SpaceCodeRepo.recentCode?.toLowerCase()) { return; } @@ -700,8 +701,7 @@ class ChatListController extends State _roomCapacitySubscription?.cancel(); MatrixState.pangeaController.subscriptionController.subscriptionNotifier .removeListener(_onSubscribe); - MatrixState.pangeaController.spaceCodeController.codeNotifier - .removeListener(_onCacheSpaceCode); + SpaceCodeController.codeNotifier.removeListener(_onCacheSpaceCode); //Pangea# scrollController.removeListener(_onScroll); super.dispose(); @@ -1112,18 +1112,14 @@ class ChatListController extends State void _initPangeaControllers(Client client) { MatrixState.pangeaController.initControllers(); if (mounted) { - MatrixState.pangeaController.spaceCodeController - .joinCachedSpaceCode(context); - MatrixState.pangeaController.spaceCodeController.codeNotifier - .addListener(_onCacheSpaceCode); + SpaceCodeController.joinCachedSpaceCode(context); + SpaceCodeController.codeNotifier.addListener(_onCacheSpaceCode); } } void _onCacheSpaceCode() { if (!mounted) return; - MatrixState.pangeaController.spaceCodeController.joinCachedSpaceCode( - context, - ); + SpaceCodeController.joinCachedSpaceCode(context); } // Pangea# diff --git a/lib/pangea/activity_sessions/activity_participant_list.dart b/lib/pangea/activity_sessions/activity_participant_list.dart index fc86daded..d983e7104 100644 --- a/lib/pangea/activity_sessions/activity_participant_list.dart +++ b/lib/pangea/activity_sessions/activity_participant_list.dart @@ -6,7 +6,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_participant_indicator.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_role_model.dart'; -import 'package:fluffychat/pangea/spaces/utils/load_participants_util.dart'; +import 'package:fluffychat/pangea/spaces/widgets/load_participants_builder.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/member_actions_popup_menu_button.dart'; diff --git a/lib/pangea/chat_list/utils/chat_list_handle_space_tap.dart b/lib/pangea/chat_list/utils/chat_list_handle_space_tap.dart index 479fd76e6..a722356b2 100644 --- a/lib/pangea/chat_list/utils/chat_list_handle_space_tap.dart +++ b/lib/pangea/chat_list/utils/chat_list_handle_space_tap.dart @@ -5,6 +5,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; +import 'package:fluffychat/pangea/spaces/space_code_repo.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; @@ -116,8 +117,7 @@ void chatListHandleSpaceTap( (element) => element.isSpace && element.membership == Membership.join, ); - final justInputtedCode = - MatrixState.pangeaController.spaceCodeController.justInputtedCode; + final justInputtedCode = SpaceCodeRepo.recentCode; if (rooms.any((s) => s.spaceChildren.any((c) => c.roomId == space.id))) { autoJoin(space); } else if (justInputtedCode != null && diff --git a/lib/pangea/chat_settings/pages/pangea_invitation_selection_view.dart b/lib/pangea/chat_settings/pages/pangea_invitation_selection_view.dart index 63d78a9dc..6af355769 100644 --- a/lib/pangea/chat_settings/pages/pangea_invitation_selection_view.dart +++ b/lib/pangea/chat_settings/pages/pangea_invitation_selection_view.dart @@ -16,7 +16,7 @@ import 'package:fluffychat/pangea/chat_settings/pages/pangea_invitation_selectio import 'package:fluffychat/pangea/common/config/environment.dart'; import 'package:fluffychat/pangea/course_plans/map_clipper.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/spaces/constants/space_constants.dart'; +import 'package:fluffychat/pangea/spaces/space_constants.dart'; import 'package:fluffychat/utils/stream_extension.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/user_dialog.dart'; import 'package:fluffychat/widgets/avatar.dart'; diff --git a/lib/pangea/chat_settings/pages/room_participants_widget.dart b/lib/pangea/chat_settings/pages/room_participants_widget.dart index 94700aed9..06e3aa848 100644 --- a/lib/pangea/chat_settings/pages/room_participants_widget.dart +++ b/lib/pangea/chat_settings/pages/room_participants_widget.dart @@ -8,7 +8,7 @@ import 'package:fluffychat/config/app_config.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/spaces/utils/load_participants_util.dart'; +import 'package:fluffychat/pangea/spaces/widgets/load_participants_builder.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/hover_builder.dart'; import 'package:fluffychat/widgets/member_actions_popup_menu_button.dart'; diff --git a/lib/pangea/choreographer/choreographer.dart b/lib/pangea/choreographer/choreographer.dart index c0a63bca2..c98ae1c2b 100644 --- a/lib/pangea/choreographer/choreographer.dart +++ b/lib/pangea/choreographer/choreographer.dart @@ -19,7 +19,7 @@ import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart' import 'package:fluffychat/pangea/events/repo/token_api_models.dart'; import 'package:fluffychat/pangea/events/repo/tokens_repo.dart'; import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; -import 'package:fluffychat/pangea/spaces/models/space_model.dart'; +import 'package:fluffychat/pangea/learning_settings/enums/tool_settings_enum.dart'; import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart'; import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index 9208816af..5f3636abb 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -20,7 +20,6 @@ import 'package:fluffychat/pangea/guard/p_vguard.dart'; import 'package:fluffychat/pangea/learning_settings/controllers/language_controller.dart'; import 'package:fluffychat/pangea/learning_settings/utils/locale_provider.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; -import 'package:fluffychat/pangea/spaces/controllers/space_code_controller.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/controllers/permissions_controller.dart'; @@ -32,7 +31,6 @@ class PangeaController { ///pangeaControllers late UserController userController; late LanguageController languageController; - late SpaceCodeController spaceCodeController; late PermissionsController permissionsController; late GetAnalyticsController getAnalytics; late PutAnalyticsController putAnalytics; @@ -75,7 +73,6 @@ class PangeaController { _addRefInObjects() { userController = UserController(this); languageController = LanguageController(this); - spaceCodeController = SpaceCodeController(this); permissionsController = PermissionsController(this); getAnalytics = GetAnalyticsController(this); putAnalytics = PutAnalyticsController(this); @@ -300,39 +297,4 @@ class PangeaController { ); } } - - // /// Joins the user to the support space if they are - // /// not already a member and have not previously left. - // Future joinSupportSpace() async { - // // if the user is already in the space, return - // await matrixState.client.roomsLoading; - // final isInSupportSpace = matrixState.client.rooms.any( - // (room) => room.id == Environment.supportSpaceId, - // ); - // if (isInSupportSpace) return; - - // // if the user has previously joined the space, return - // final bool previouslyJoined = - // userController.profile.userSettings.hasJoinedHelpSpace ?? false; - // if (previouslyJoined) return; - - // // join the space - // try { - // await matrixState.client.joinRoomById(Environment.supportSpaceId); - // final room = matrixState.client.getRoomById(Environment.supportSpaceId); - // if (room == null) { - // await matrixState.client.waitForRoomInSync( - // Environment.supportSpaceId, - // join: true, - // ); - // } - // userController.updateProfile((profile) { - // profile.userSettings.hasJoinedHelpSpace = true; - // return profile; - // }); - // } catch (err, s) { - // ErrorHandler.logError(e: err, s: s); - // return; - // } - // } } diff --git a/lib/pangea/common/widgets/share_room_button.dart b/lib/pangea/common/widgets/share_room_button.dart index 04ec59091..b698ef703 100644 --- a/lib/pangea/common/widgets/share_room_button.dart +++ b/lib/pangea/common/widgets/share_room_button.dart @@ -9,7 +9,7 @@ import 'package:universal_html/html.dart' as html; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/spaces/constants/space_constants.dart'; +import 'package:fluffychat/pangea/spaces/space_constants.dart'; class ShareRoomButton extends StatelessWidget { final Room room; diff --git a/lib/pangea/course_chats/course_chats_page.dart b/lib/pangea/course_chats/course_chats_page.dart index 1c9af4e2f..e909fc1c1 100644 --- a/lib/pangea/course_chats/course_chats_page.dart +++ b/lib/pangea/course_chats/course_chats_page.dart @@ -21,7 +21,7 @@ import 'package:fluffychat/pangea/course_plans/courses/course_plan_builder.dart' import 'package:fluffychat/pangea/course_plans/courses/course_plan_room_extension.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/public_spaces/public_room_bottom_sheet.dart'; -import 'package:fluffychat/pangea/spaces/constants/space_constants.dart'; +import 'package:fluffychat/pangea/spaces/space_constants.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart'; diff --git a/lib/pangea/course_chats/course_default_chats_enum.dart b/lib/pangea/course_chats/course_default_chats_enum.dart index 7392d2e57..a8390cb58 100644 --- a/lib/pangea/course_chats/course_default_chats_enum.dart +++ b/lib/pangea/course_chats/course_default_chats_enum.dart @@ -1,5 +1,5 @@ import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/spaces/constants/space_constants.dart'; +import 'package:fluffychat/pangea/spaces/space_constants.dart'; enum CourseDefaultChatsEnum { introductions, diff --git a/lib/pangea/course_plans/courses/course_plan_room_extension.dart b/lib/pangea/course_plans/courses/course_plan_room_extension.dart index e9d78c2b5..1915e2792 100644 --- a/lib/pangea/course_plans/courses/course_plan_room_extension.dart +++ b/lib/pangea/course_plans/courses/course_plan_room_extension.dart @@ -15,7 +15,7 @@ import 'package:fluffychat/pangea/course_plans/courses/course_plan_event.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/extensions/join_rule_extension.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/spaces/constants/space_constants.dart'; +import 'package:fluffychat/pangea/spaces/space_constants.dart'; extension CoursePlanRoomExtension on Room { CoursePlanEvent? get coursePlan { diff --git a/lib/pangea/course_settings/topic_participant_list.dart b/lib/pangea/course_settings/topic_participant_list.dart index 9ba87f871..1490946ac 100644 --- a/lib/pangea/course_settings/topic_participant_list.dart +++ b/lib/pangea/course_settings/topic_participant_list.dart @@ -6,7 +6,7 @@ import 'package:collection/collection.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/spaces/utils/load_participants_util.dart'; +import 'package:fluffychat/pangea/spaces/widgets/load_participants_builder.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/member_actions_popup_menu_button.dart'; diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index 621986924..d0568cdc2 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -18,8 +18,8 @@ import 'package:fluffychat/pangea/events/repo/language_detection_repo.dart'; import 'package:fluffychat/pangea/events/repo/language_detection_request.dart'; import 'package:fluffychat/pangea/events/repo/language_detection_response.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; +import 'package:fluffychat/pangea/learning_settings/enums/tool_settings_enum.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; -import 'package:fluffychat/pangea/spaces/models/space_model.dart'; import 'package:fluffychat/pangea/speech_to_text/audio_encoding_enum.dart'; import 'package:fluffychat/pangea/speech_to_text/speech_to_text_repo.dart'; import 'package:fluffychat/pangea/speech_to_text/speech_to_text_request_model.dart'; diff --git a/lib/pangea/extensions/join_rule_extension.dart b/lib/pangea/extensions/join_rule_extension.dart index 18c77103e..97981f6d6 100644 --- a/lib/pangea/extensions/join_rule_extension.dart +++ b/lib/pangea/extensions/join_rule_extension.dart @@ -2,7 +2,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/spaces/utils/space_code.dart'; +import 'package:fluffychat/pangea/spaces/utils/space_code_extension.dart'; extension JoinRuleExtension on Client { Future pangeaJoinRules( @@ -11,7 +11,7 @@ extension JoinRuleExtension on Client { }) async { String? joinCode; try { - joinCode = await SpaceCodeUtil.generateSpaceCode(this); + joinCode = await requestSpaceCode(); } catch (e, s) { ErrorHandler.logError( e: e, @@ -42,7 +42,7 @@ extension JoinRuleExtensionOnRoom on Room { final currentJoinRules = getState(EventTypes.RoomJoinRules)?.content ?? {}; if (currentJoinRules[ModelKey.accessCode] != null) return; - final joinCode = await SpaceCodeUtil.generateSpaceCode(client); + final joinCode = await client.requestSpaceCode(); currentJoinRules[ModelKey.accessCode] = joinCode; await client.setRoomStateWithKey( diff --git a/lib/pangea/extensions/pangea_room_extension.dart b/lib/pangea/extensions/pangea_room_extension.dart index d414c7bf2..73a07cbaf 100644 --- a/lib/pangea/extensions/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension.dart @@ -31,7 +31,7 @@ import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart' import 'package:fluffychat/pangea/extensions/join_rule_extension.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/lemmas/user_set_lemma_info.dart'; -import 'package:fluffychat/pangea/spaces/constants/space_constants.dart'; +import 'package:fluffychat/pangea/spaces/space_constants.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import '../choreographer/choreo_record_model.dart'; diff --git a/lib/pangea/extensions/pangea_rooms_chunk_extension.dart b/lib/pangea/extensions/pangea_rooms_chunk_extension.dart index 78785948f..8906094e8 100644 --- a/lib/pangea/extensions/pangea_rooms_chunk_extension.dart +++ b/lib/pangea/extensions/pangea_rooms_chunk_extension.dart @@ -2,7 +2,7 @@ import 'dart:math'; import 'package:matrix/matrix_api_lite/generated/model.dart'; -import 'package:fluffychat/pangea/spaces/constants/space_constants.dart'; +import 'package:fluffychat/pangea/spaces/space_constants.dart'; extension PangeaRoomsChunk on PublicRoomsChunk { /// Use Random with a seed to get the default diff --git a/lib/pangea/learning_settings/enums/tool_settings_enum.dart b/lib/pangea/learning_settings/enums/tool_settings_enum.dart new file mode 100644 index 000000000..3533db831 --- /dev/null +++ b/lib/pangea/learning_settings/enums/tool_settings_enum.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; + +import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/widgets/matrix.dart'; + +enum ToolSetting { + interactiveTranslator, + interactiveGrammar, + immersionMode, + definitions, + autoIGC, + enableTTS, + enableAutocorrect; + + String toolName(BuildContext context) { + switch (this) { + case ToolSetting.interactiveTranslator: + return L10n.of(context).interactiveTranslatorSliderHeader; + case ToolSetting.interactiveGrammar: + return L10n.of(context).interactiveGrammarSliderHeader; + case ToolSetting.immersionMode: + return L10n.of(context).toggleImmersionMode; + case ToolSetting.definitions: + return L10n.of(context).definitionsToolName; + case ToolSetting.autoIGC: + return L10n.of(context).autoIGCToolName; + case ToolSetting.enableTTS: + return L10n.of(context).enableTTSToolName; + case ToolSetting.enableAutocorrect: + return L10n.of(context).enableAutocorrectToolName; + } + } + + //use l10n to get tool name + String toolDescription(BuildContext context) { + switch (this) { + case ToolSetting.interactiveTranslator: + return L10n.of(context).itToggleDescription; + case ToolSetting.interactiveGrammar: + return L10n.of(context).igcToggleDescription; + case ToolSetting.immersionMode: + return L10n.of(context).toggleImmersionModeDesc; + case ToolSetting.definitions: + return L10n.of(context).definitionsToolDescription; + case ToolSetting.autoIGC: + return L10n.of(context).autoIGCToolDescription; + case ToolSetting.enableTTS: + return L10n.of(context).enableTTSToolDescription; + case ToolSetting.enableAutocorrect: + return L10n.of(context).enableAutocorrectDescription; + } + } + + bool get isAvailableSetting { + switch (this) { + case ToolSetting.interactiveTranslator: + case ToolSetting.interactiveGrammar: + case ToolSetting.definitions: + case ToolSetting.immersionMode: + return false; + case ToolSetting.autoIGC: + case ToolSetting.enableTTS: + case ToolSetting.enableAutocorrect: + return true; + } + } + + bool get enabled => + MatrixState.pangeaController.permissionsController.isToolEnabled(this); +} diff --git a/lib/pangea/learning_settings/pages/settings_learning.dart b/lib/pangea/learning_settings/pages/settings_learning.dart index e894bca95..5225bbc7b 100644 --- a/lib/pangea/learning_settings/pages/settings_learning.dart +++ b/lib/pangea/learning_settings/pages/settings_learning.dart @@ -10,10 +10,10 @@ import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/instructions/instruction_settings.dart'; import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; +import 'package:fluffychat/pangea/learning_settings/enums/tool_settings_enum.dart'; import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; import 'package:fluffychat/pangea/learning_settings/pages/settings_learning_view.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; -import 'package:fluffychat/pangea/spaces/models/space_model.dart'; import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; import 'package:fluffychat/pangea/user/models/user_model.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; diff --git a/lib/pangea/learning_settings/pages/settings_learning_view.dart b/lib/pangea/learning_settings/pages/settings_learning_view.dart index 952d50d39..0b7d32014 100644 --- a/lib/pangea/learning_settings/pages/settings_learning_view.dart +++ b/lib/pangea/learning_settings/pages/settings_learning_view.dart @@ -13,12 +13,12 @@ import 'package:fluffychat/pangea/chat_settings/widgets/language_level_dropdown. import 'package:fluffychat/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart'; import 'package:fluffychat/pangea/instructions/reset_instructions_list_tile.dart'; +import 'package:fluffychat/pangea/learning_settings/enums/tool_settings_enum.dart'; import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart'; import 'package:fluffychat/pangea/learning_settings/widgets/country_picker_tile.dart'; import 'package:fluffychat/pangea/learning_settings/widgets/p_language_dropdown.dart'; import 'package:fluffychat/pangea/learning_settings/widgets/p_settings_switch_list_tile.dart'; -import 'package:fluffychat/pangea/spaces/models/space_model.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import 'package:fluffychat/widgets/matrix.dart'; diff --git a/lib/pangea/login/pages/course_code_page.dart b/lib/pangea/login/pages/course_code_page.dart index ab232f4e5..5052747e3 100644 --- a/lib/pangea/login/pages/course_code_page.dart +++ b/lib/pangea/login/pages/course_code_page.dart @@ -6,6 +6,7 @@ import 'package:go_router/go_router.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/login/pages/add_course_page.dart'; +import 'package:fluffychat/pangea/spaces/space_code_controller.dart'; import 'package:fluffychat/widgets/matrix.dart'; class CourseCodePage extends StatefulWidget { @@ -39,8 +40,7 @@ class CourseCodePageState extends State { return; } - final roomId = await MatrixState.pangeaController.spaceCodeController - .joinSpaceWithCode( + final roomId = await SpaceCodeController.joinSpaceWithCode( context, _code, ); diff --git a/lib/pangea/login/pages/create_pangea_account_page.dart b/lib/pangea/login/pages/create_pangea_account_page.dart index 53b1fd641..73a9da82e 100644 --- a/lib/pangea/login/pages/create_pangea_account_page.dart +++ b/lib/pangea/login/pages/create_pangea_account_page.dart @@ -15,6 +15,8 @@ import 'package:fluffychat/pangea/course_plans/courses/course_plans_repo.dart'; import 'package:fluffychat/pangea/course_plans/courses/get_localized_courses_request.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; import 'package:fluffychat/pangea/login/utils/lang_code_repo.dart'; +import 'package:fluffychat/pangea/spaces/space_code_controller.dart'; +import 'package:fluffychat/pangea/spaces/space_code_repo.dart'; import 'package:fluffychat/widgets/matrix.dart'; class CreatePangeaAccountPage extends StatefulWidget { @@ -52,8 +54,7 @@ class CreatePangeaAccountPageState extends State { (await _cachedLangCode)?.baseLangCode ?? MatrixState.pangeaController.languageController.systemLanguage?.langCode; - String? get _cachedSpaceCode => - MatrixState.pangeaController.spaceCodeController.cachedSpaceCode; + String? get _cachedSpaceCode => SpaceCodeRepo.spaceCode; Future _createProfile() async { setState(() { @@ -72,13 +73,10 @@ class CreatePangeaAccountPageState extends State { } Future _joinCachedCourse() async { - await MatrixState.pangeaController.spaceCodeController.initCompleter.future; if (_cachedSpaceCode == null) return; try { - final spaceId = await MatrixState.pangeaController.spaceCodeController - .joinCachedSpaceCode(context); - + final spaceId = await SpaceCodeController.joinCachedSpaceCode(context); if (spaceId == null) { throw Exception('Failed to join space with code $_cachedSpaceCode'); } diff --git a/lib/pangea/login/pages/login_or_signup_view.dart b/lib/pangea/login/pages/login_or_signup_view.dart index 39774f615..7482f55b5 100644 --- a/lib/pangea/login/pages/login_or_signup_view.dart +++ b/lib/pangea/login/pages/login_or_signup_view.dart @@ -7,7 +7,7 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; import 'package:fluffychat/pangea/common/widgets/pangea_logo_svg.dart'; import 'package:fluffychat/pangea/login/widgets/app_config_dialog.dart'; -import 'package:fluffychat/widgets/matrix.dart'; +import 'package:fluffychat/pangea/spaces/space_code_repo.dart'; class LoginOrSignupView extends StatefulWidget { const LoginOrSignupView({super.key}); @@ -25,8 +25,7 @@ class LoginOrSignupViewState extends State { _loadOverrides(); } - String? get _cachedSpaceCode => - MatrixState.pangeaController.spaceCodeController.cachedSpaceCode; + String? get _cachedSpaceCode => SpaceCodeRepo.spaceCode; Future _loadOverrides() async { final overrides = await Environment.getAppConfigOverrides(); diff --git a/lib/pangea/public_spaces/public_room_bottom_sheet.dart b/lib/pangea/public_spaces/public_room_bottom_sheet.dart index bbd9b5904..7fcbd7219 100644 --- a/lib/pangea/public_spaces/public_room_bottom_sheet.dart +++ b/lib/pangea/public_spaces/public_room_bottom_sheet.dart @@ -7,6 +7,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/extensions/pangea_rooms_chunk_extension.dart'; +import 'package:fluffychat/pangea/spaces/space_code_controller.dart'; import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; @@ -83,8 +84,7 @@ class PublicRoomBottomSheetState extends State { bool get _isKnockRoom => widget.chunk?.joinRule == 'knock'; Future _joinWithCode() async { - final resp = await MatrixState.pangeaController.spaceCodeController - .joinSpaceWithCode( + final resp = await SpaceCodeController.joinSpaceWithCode( context, _codeController.text, notFoundError: L10n.of(context).notTheCodeError, diff --git a/lib/pangea/spaces/widgets/download_space_analytics_dialog.dart b/lib/pangea/space_analytics/download_space_analytics_dialog.dart similarity index 100% rename from lib/pangea/spaces/widgets/download_space_analytics_dialog.dart rename to lib/pangea/space_analytics/download_space_analytics_dialog.dart diff --git a/lib/pangea/space_analytics/space_analytics_view.dart b/lib/pangea/space_analytics/space_analytics_view.dart index 33f34f17d..91a7daf7b 100644 --- a/lib/pangea/space_analytics/space_analytics_view.dart +++ b/lib/pangea/space_analytics/space_analytics_view.dart @@ -8,9 +8,9 @@ import 'package:material_symbols_icons/symbols.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/widgets/dropdown_text_button.dart'; import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; +import 'package:fluffychat/pangea/space_analytics/download_space_analytics_dialog.dart'; import 'package:fluffychat/pangea/space_analytics/space_analytics.dart'; import 'package:fluffychat/pangea/space_analytics/space_analytics_download_enum.dart'; -import 'package:fluffychat/pangea/spaces/widgets/download_space_analytics_dialog.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; diff --git a/lib/pangea/spaces/models/space_model.dart b/lib/pangea/spaces/models/space_model.dart deleted file mode 100644 index e1cd18e81..000000000 --- a/lib/pangea/spaces/models/space_model.dart +++ /dev/null @@ -1,299 +0,0 @@ -import 'dart:developer'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -import 'package:matrix/matrix.dart'; - -import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/spaces/constants/space_constants.dart'; -import 'package:fluffychat/widgets/matrix.dart'; -import '../../events/constants/pangea_event_types.dart'; -import '../../learning_settings/constants/language_constants.dart'; -import '../../learning_settings/models/language_model.dart'; - -class LanguageSettingsModel { - String? city; - String? country; - String? schoolName; - String dominantLanguage; - String targetLanguage; - - LanguageSettingsModel({ - this.dominantLanguage = SpaceConstants.defaultDominantLanguage, - this.targetLanguage = SpaceConstants.defaultTargetLanguage, - this.city, - this.country, - this.schoolName, - }); - - factory LanguageSettingsModel.fromJson(Map json) { - return LanguageSettingsModel( - city: json['city'], - country: json['country'], - dominantLanguage: LanguageModel.codeFromNameOrCode( - json['dominant_language'] ?? LanguageKeys.unknownLanguage, - ), - targetLanguage: LanguageModel.codeFromNameOrCode( - json['target_language'] ?? LanguageKeys.unknownLanguage, - ), - schoolName: json['school_name'], - ); - } - - Map toJson() { - final data = {}; - try { - data['city'] = city; - data['country'] = country; - //check for and do "english" => en and "spanish" => es - data['dominant_language'] = dominantLanguage; - //check for and do "english" => en and "spanish" => es - data['target_language'] = targetLanguage; - data['school_name'] = schoolName; - return data; - } catch (e, s) { - debugger(when: kDebugMode); - ErrorHandler.logError( - e: e, - s: s, - data: data, - ); - return data; - } - } - - StateEvent get toStateEvent => StateEvent( - content: toJson(), - type: PangeaEventTypes.languageSettings, - ); -} - -class PangeaRoomRules { - // int? pangeaClassID; // this is id our database - bool isPublic; - bool isOpenEnrollment; - bool oneToOneChatClass; - bool isCreateRooms; - bool isShareVideo; - bool isSharePhoto; - bool isShareFiles; - bool isShareLocation; - bool isCreateStories; - bool isVoiceNotes; - bool isInviteOnlyStudents; - // 0 = forbidden, 1 = allow individual to choose, 2 = require - int interactiveTranslator; - int interactiveGrammar; - int immersionMode; - int definitions; - int translations; - int autoIGC; - - PangeaRoomRules({ - this.isPublic = false, - this.isOpenEnrollment = false, - this.oneToOneChatClass = true, - this.isCreateRooms = true, - this.isShareVideo = true, - this.isSharePhoto = true, - this.isShareFiles = true, - this.isShareLocation = false, - this.isCreateStories = false, - this.isVoiceNotes = true, - this.isInviteOnlyStudents = true, - this.interactiveTranslator = SpaceConstants.languageToolPermissions, - this.interactiveGrammar = SpaceConstants.languageToolPermissions, - this.immersionMode = SpaceConstants.languageToolPermissions, - this.definitions = SpaceConstants.languageToolPermissions, - this.translations = SpaceConstants.languageToolPermissions, - this.autoIGC = SpaceConstants.languageToolPermissions, - }); - - setLanguageToolSetting(ToolSetting setting, int value) { - switch (setting) { - case ToolSetting.interactiveTranslator: - interactiveTranslator = value; - break; - case ToolSetting.interactiveGrammar: - interactiveGrammar = value; - break; - case ToolSetting.immersionMode: - immersionMode = value; - break; - case ToolSetting.definitions: - definitions = value; - break; - // case ToolSetting.translations: - // translations = value; - // break; - case ToolSetting.autoIGC: - autoIGC = value; - break; - default: - throw Exception('Invalid key for setting permissions - $setting'); - } - } - - StateEvent get toStateEvent => StateEvent( - content: toJson(), - type: PangeaEventTypes.rules, - ); - - factory PangeaRoomRules.fromJson(Map json) => - PangeaRoomRules( - // pangeaClassID: json['pangea_class']; - isPublic: json['is_public'], - isOpenEnrollment: json['is_open_enrollment'], - oneToOneChatClass: json['one_to_one_chat_class'], - isCreateRooms: json['is_create_rooms'], - isShareVideo: json['is_share_video'], - isSharePhoto: json['is_share_photo'], - isShareFiles: json['is_share_files'], - isShareLocation: json['is_share_location'], - isCreateStories: json['is_create_stories'], - isVoiceNotes: json['is_voice_notes'], - isInviteOnlyStudents: json['is_invite_only_students'] ?? true, - interactiveTranslator: json['interactive_translator'] ?? - SpaceConstants.languageToolPermissions, - interactiveGrammar: json['interactive_grammar'] ?? - SpaceConstants.languageToolPermissions, - immersionMode: - json['immersion_mode'] ?? SpaceConstants.languageToolPermissions, - definitions: - json['definitions'] ?? SpaceConstants.languageToolPermissions, - translations: - json['translations'] ?? SpaceConstants.languageToolPermissions, - autoIGC: json['auto_igc'] ?? SpaceConstants.languageToolPermissions, - ); - - Map toJson() { - final data = {}; - // data['pangea_class'] = pangeaClassID; - data['is_public'] = isPublic; - data['is_open_enrollment'] = isOpenEnrollment; - data['one_to_one_chat_class'] = oneToOneChatClass; - data['is_create_rooms'] = isCreateRooms; - data['is_share_video'] = isShareVideo; - data['is_share_photo'] = isSharePhoto; - data['is_share_files'] = isShareFiles; - data['is_share_location'] = isShareLocation; - data['is_create_stories'] = isCreateStories; - data['is_voice_notes'] = isVoiceNotes; - data['is_invite_only_students'] = isInviteOnlyStudents; - data['interactive_translator'] = interactiveTranslator; - data['interactive_grammar'] = interactiveGrammar; - data['immersion_mode'] = immersionMode; - data['definitions'] = definitions; - data['translations'] = translations; - data['auto_igc'] = autoIGC; - return data; - } - - int getToolSettings(ToolSetting setting) { - switch (setting) { - case ToolSetting.interactiveTranslator: - return interactiveTranslator; - case ToolSetting.interactiveGrammar: - return interactiveGrammar; - case ToolSetting.immersionMode: - return immersionMode; - case ToolSetting.definitions: - return definitions; - // case ToolSetting.translations: - // return translations; - case ToolSetting.autoIGC: - return autoIGC; - default: - throw Exception('Invalid key for setting permissions - $setting'); - } - } - - String languageToolPermissionsText( - BuildContext context, - ToolSetting setting, - ) { - switch (getToolSettings(setting)) { - case 0: - return L10n.of(context).interactiveTranslatorNotAllowed; - case 1: - return L10n.of(context).interactiveTranslatorAllowed; - case 2: - return L10n.of(context).interactiveTranslatorRequired; - default: - return L10n.of(context).notYetSet; - } - } -} - -enum ToolSetting { - interactiveTranslator, - interactiveGrammar, - immersionMode, - definitions, - // translations, - autoIGC, - enableTTS, - enableAutocorrect; - - String toolName(BuildContext context) { - switch (this) { - case ToolSetting.interactiveTranslator: - return L10n.of(context).interactiveTranslatorSliderHeader; - case ToolSetting.interactiveGrammar: - return L10n.of(context).interactiveGrammarSliderHeader; - case ToolSetting.immersionMode: - return L10n.of(context).toggleImmersionMode; - case ToolSetting.definitions: - return L10n.of(context).definitionsToolName; - // case ToolSetting.translations: - // return L10n.of(context).messageTranslationsToolName; - case ToolSetting.autoIGC: - return L10n.of(context).autoIGCToolName; - case ToolSetting.enableTTS: - return L10n.of(context).enableTTSToolName; - case ToolSetting.enableAutocorrect: - return L10n.of(context).enableAutocorrectToolName; - } - } - - //use l10n to get tool name - String toolDescription(BuildContext context) { - switch (this) { - case ToolSetting.interactiveTranslator: - return L10n.of(context).itToggleDescription; - case ToolSetting.interactiveGrammar: - return L10n.of(context).igcToggleDescription; - case ToolSetting.immersionMode: - return L10n.of(context).toggleImmersionModeDesc; - case ToolSetting.definitions: - return L10n.of(context).definitionsToolDescription; - // case ToolSetting.translations: - // return L10n.of(context).translationsToolDescrption; - case ToolSetting.autoIGC: - return L10n.of(context).autoIGCToolDescription; - case ToolSetting.enableTTS: - return L10n.of(context).enableTTSToolDescription; - case ToolSetting.enableAutocorrect: - return L10n.of(context).enableAutocorrectDescription; - } - } - - bool get isAvailableSetting { - switch (this) { - case ToolSetting.interactiveTranslator: - case ToolSetting.interactiveGrammar: - case ToolSetting.definitions: - case ToolSetting.immersionMode: - return false; - case ToolSetting.autoIGC: - case ToolSetting.enableTTS: - case ToolSetting.enableAutocorrect: - return true; - } - } - - bool get enabled => - MatrixState.pangeaController.permissionsController.isToolEnabled(this); -} diff --git a/lib/pangea/spaces/controllers/space_code_controller.dart b/lib/pangea/spaces/space_code_controller.dart similarity index 67% rename from lib/pangea/spaces/controllers/space_code_controller.dart rename to lib/pangea/spaces/space_code_controller.dart index 9fe95ba0c..7fce10adb 100644 --- a/lib/pangea/spaces/controllers/space_code_controller.dart +++ b/lib/pangea/spaces/space_code_controller.dart @@ -5,85 +5,61 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; -import 'package:get_storage/get_storage.dart'; import 'package:go_router/go_router.dart'; -import 'package:http/http.dart'; +import 'package:http/http.dart' hide Client; import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/common/constants/local.key.dart'; -import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; -import 'package:fluffychat/pangea/spaces/constants/space_constants.dart'; +import 'package:fluffychat/pangea/spaces/space_code_repo.dart'; +import 'package:fluffychat/pangea/spaces/space_constants.dart'; import 'package:fluffychat/pangea/spaces/utils/knock_space_extension.dart'; import 'package:fluffychat/pangea/spaces/widgets/too_many_requests_dialog.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import '../../common/controllers/base_controller.dart'; +import '../common/controllers/base_controller.dart'; class NotFoundException implements Exception {} class SpaceCodeController extends BaseController { - late PangeaController _pangeaController; - static final GetStorage _spaceStorage = GetStorage('class_storage'); + static ValueNotifier codeNotifier = ValueNotifier(null); - Completer initCompleter = Completer(); - ValueNotifier codeNotifier = ValueNotifier(null); - - SpaceCodeController(PangeaController pangeaController) : super() { - _pangeaController = pangeaController; - GetStorage.init('class_storage').then( - (_) => initCompleter.complete(), - ); + static Future onOpenAppViaUrl(Uri url) async { + if (url.fragment.isEmpty) return; + final fragment = Uri.parse(url.fragment); + final code = fragment.queryParameters[SpaceConstants.classCode]; + if (code != null && fragment.path.contains('join_with_link')) { + await SpaceCodeRepo.setSpaceCode(code); + codeNotifier.value = code; + } } - Future cacheSpaceCode(String code) async { - if (code.isEmpty) return; - await _spaceStorage.write( - PLocalKey.cachedSpaceCodeToJoin, - code, - ); - codeNotifier.value = code; - } - - String? get justInputtedCode => - _spaceStorage.read(PLocalKey.justInputtedCode); - - String? get cachedSpaceCode => - _spaceStorage.read(PLocalKey.cachedSpaceCodeToJoin); - - Future joinCachedSpaceCode(BuildContext context) async { - final String? spaceCode = cachedSpaceCode; + static Future joinCachedSpaceCode(BuildContext context) async { + final String? spaceCode = SpaceCodeRepo.spaceCode; if (spaceCode == null) return null; final spaceId = await joinSpaceWithCode( context, spaceCode, ); - await _spaceStorage.remove( - PLocalKey.cachedSpaceCodeToJoin, - ); - + await SpaceCodeRepo.clearSpaceCode(); if (spaceId != null) { - final room = _pangeaController.matrixState.client.getRoomById(spaceId); + final room = + MatrixState.pangeaController.matrixState.client.getRoomById(spaceId); room?.isSpace ?? true ? context.go('/rooms/spaces/$spaceId/details') : context.go('/rooms/${room?.id}'); return spaceId; } - return null; } - Future joinSpaceWithCode( + static Future joinSpaceWithCode( BuildContext context, String spaceCode, { String? notFoundError, }) async { - final client = _pangeaController.matrixState.client; - await _spaceStorage.write( - PLocalKey.justInputtedCode, - spaceCode, - ); + final client = MatrixState.pangeaController.matrixState.client; + await SpaceCodeRepo.setRecentCode(spaceCode); final resp = await showFutureLoadingDialog( context: context, @@ -148,8 +124,8 @@ class SpaceCodeController extends BaseController { return roomIdToJoin; } - Future _joinSpace(String spaceId) async { - final client = _pangeaController.matrixState.client; + static Future _joinSpace(String spaceId) async { + final client = MatrixState.pangeaController.matrixState.client; await client.joinRoomById(spaceId); Room? room = client.getRoomById(spaceId); @@ -180,14 +156,4 @@ class SpaceCodeController extends BaseController { await room.requestParticipants(); } } - - static Future onOpenAppViaUrl(Uri url) async { - if (url.fragment.isEmpty) return; - final fragment = Uri.parse(url.fragment); - final code = fragment.queryParameters[SpaceConstants.classCode]; - if (code != null && fragment.path.contains('join_with_link')) { - await MatrixState.pangeaController.spaceCodeController - .cacheSpaceCode(code); - } - } } diff --git a/lib/pangea/spaces/space_code_repo.dart b/lib/pangea/spaces/space_code_repo.dart new file mode 100644 index 000000000..af6ab3fd8 --- /dev/null +++ b/lib/pangea/spaces/space_code_repo.dart @@ -0,0 +1,36 @@ +import 'package:get_storage/get_storage.dart'; + +import 'package:fluffychat/pangea/common/constants/local.key.dart'; + +class SpaceCodeRepo { + static final GetStorage _spaceStorage = GetStorage('class_storage'); + + static String? get spaceCode => + _spaceStorage.read(PLocalKey.cachedSpaceCodeToJoin); + + static String? get recentCode => + _spaceStorage.read(PLocalKey.justInputtedCode); + + static Future setSpaceCode(String code) async { + if (code.isEmpty) return; + await _spaceStorage.write( + PLocalKey.cachedSpaceCodeToJoin, + code, + ); + } + + static Future setRecentCode(String code) async { + await _spaceStorage.write( + PLocalKey.justInputtedCode, + code, + ); + } + + static Future clearSpaceCode() async { + await _spaceStorage.remove(PLocalKey.cachedSpaceCodeToJoin); + } + + static Future clearRecentCode() async { + await _spaceStorage.remove(PLocalKey.justInputtedCode); + } +} diff --git a/lib/pangea/spaces/constants/space_constants.dart b/lib/pangea/spaces/space_constants.dart similarity index 88% rename from lib/pangea/spaces/constants/space_constants.dart rename to lib/pangea/spaces/space_constants.dart index acbd9501d..5dde4e2e2 100644 --- a/lib/pangea/spaces/constants/space_constants.dart +++ b/lib/pangea/spaces/space_constants.dart @@ -2,9 +2,6 @@ import 'package:fluffychat/config/app_config.dart'; class SpaceConstants { static const powerLevelOfAdmin = 100; - static const languageToolPermissions = 1; - static const defaultDominantLanguage = "en"; - static const defaultTargetLanguage = "es"; static const String classCode = 'classcode'; static const String introductionChatAlias = 'introductionChat'; static const String announcementsChatAlias = 'announcementsChat'; diff --git a/lib/pangea/spaces/utils/space_code.dart b/lib/pangea/spaces/utils/space_code.dart deleted file mode 100644 index c15835aec..000000000 --- a/lib/pangea/spaces/utils/space_code.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'dart:convert'; - -import 'package:matrix/matrix.dart'; - -class SpaceCodeUtil { - static Future generateSpaceCode(Client client) async { - final response = await client.httpClient.get( - Uri.parse( - '${client.homeserver}/_synapse/client/pangea/v1/request_room_code', - ), - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer ${client.accessToken}', - }, - ); - if (response.statusCode != 200) { - throw Exception('Failed to generate room code: $response'); - } - final roomCodeResult = jsonDecode(response.body); - if (roomCodeResult['access_code'] is String) { - return roomCodeResult['access_code'] as String; - } else { - throw Exception('Invalid response, access_code not found $response'); - } - } -} diff --git a/lib/pangea/spaces/utils/space_code_extension.dart b/lib/pangea/spaces/utils/space_code_extension.dart new file mode 100644 index 000000000..2fa43262d --- /dev/null +++ b/lib/pangea/spaces/utils/space_code_extension.dart @@ -0,0 +1,34 @@ +import 'dart:convert'; + +import 'package:http/http.dart' hide Client; +import 'package:matrix/matrix.dart'; +import 'package:matrix/matrix_api_lite/generated/api.dart'; + +extension SpaceCodeExtension on Api { + Future getSpaceCode() async { + final requestUri = Uri( + path: '/_synapse/client/pangea/v1/request_room_code', + ); + final request = Request('GET', baseUri!.resolveUri(requestUri)); + request.headers['content-type'] = 'application/json'; + request.headers['authorization'] = 'Bearer ${bearerToken!}'; + final response = await httpClient.send(request); + final responseBody = await response.stream.toBytes(); + final responseString = utf8.decode(responseBody); + if (response.statusCode != 200) { + throw Exception( + 'HTTP error response: statusCode=${response.statusCode}, body=$responseString', + ); + } + final json = jsonDecode(responseString); + if (json['access_code'] is String) { + return json['access_code'] as String; + } else { + throw Exception('Invalid response, access_code not found $response'); + } + } +} + +extension SpaceCodeRequest on Client { + Future requestSpaceCode() => getSpaceCode(); +} diff --git a/lib/pangea/spaces/utils/join_with_link.dart b/lib/pangea/spaces/widgets/join_with_link_page.dart similarity index 88% rename from lib/pangea/spaces/utils/join_with_link.dart rename to lib/pangea/spaces/widgets/join_with_link_page.dart index b37f750b4..b206e7825 100644 --- a/lib/pangea/spaces/utils/join_with_link.dart +++ b/lib/pangea/spaces/widgets/join_with_link_page.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:fluffychat/widgets/matrix.dart'; +import 'package:fluffychat/pangea/spaces/space_code_controller.dart'; //if on home with classcode in url and not logged in, then save it soemhow and after llogin, join class automatically //if on home with classcode in url and logged in, then join class automatically @@ -33,8 +33,7 @@ class _JoinClassWithLinkState extends State { } if (widget.classCode != null) { - await MatrixState.pangeaController.spaceCodeController - .cacheSpaceCode(widget.classCode!); + await SpaceCodeController.cacheSpaceCode(widget.classCode!); } context.push("/home"); }); diff --git a/lib/pangea/spaces/widgets/leaderboard_participant_list.dart b/lib/pangea/spaces/widgets/leaderboard_participant_list.dart deleted file mode 100644 index 89c951279..000000000 --- a/lib/pangea/spaces/widgets/leaderboard_participant_list.dart +++ /dev/null @@ -1,153 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:matrix/matrix.dart'; - -import 'package:fluffychat/config/themes.dart'; -import 'package:fluffychat/pages/chat_list/status_msg_list.dart'; -import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; -import 'package:fluffychat/pangea/spaces/utils/load_participants_util.dart'; -import 'package:fluffychat/utils/stream_extension.dart'; -import 'package:fluffychat/widgets/adaptive_dialogs/user_dialog.dart'; -import 'package:fluffychat/widgets/matrix.dart'; -import 'package:fluffychat/widgets/presence_builder.dart'; - -class LeaderboardParticipantList extends StatefulWidget { - final Room space; - - const LeaderboardParticipantList({ - required this.space, - super.key, - }); - - static const double height = 116; - - @override - State createState() => - LeaderboardParticipantListState(); -} - -class LeaderboardParticipantListState - extends State { - final _scrollController = ScrollController(); - - @override - void dispose() { - _scrollController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final client = Matrix.of(context).client; - final theme = Theme.of(context); - - return StreamBuilder( - stream: client.onSync.stream.rateLimit(const Duration(seconds: 3)), - builder: (context, snapshot) { - return LoadParticipantsBuilder( - room: widget.space, - loadProfiles: true, - builder: (context, participantsLoader) { - final participants = participantsLoader.sortedParticipants - .where((p) => p.membership == Membership.join) - .toList(); - - return AnimatedSize( - duration: FluffyThemes.animationDuration, - curve: Curves.easeInOut, - child: SizedBox( - height: 130.0, - child: Scrollbar( - controller: _scrollController, - child: ListView.builder( - controller: _scrollController, - padding: const EdgeInsets.fromLTRB( - 8.0, - 8.0, - 8.0, - 16.0, - ), - scrollDirection: Axis.horizontal, - itemCount: participants.length, - itemBuilder: (context, i) { - final user = participants[i]; - final publicProfile = - participantsLoader.getAnalyticsProfile( - user.id, - ); - - LinearGradient? gradient = i.leaderboardGradient; - - if (user.id == BotName.byEnvironment || - publicProfile == null || - publicProfile.level == null) { - gradient = null; - } - - return PresenceBuilder( - userId: user.id, - builder: (context, presence) { - Color? dotColor; - if (presence != null) { - dotColor = presence.presence.isOnline - ? Colors.green - : presence.presence.isUnavailable - ? Colors.orange - : Colors.grey; - } - - return PresenceAvatar( - presence: presence ?? - CachedPresence( - PresenceType.unavailable, - null, - null, - null, - user.id, - ), - height: StatusMessageList.height, - onTap: (profile) => UserDialog.show( - context: context, - profile: profile, - ), - gradient: gradient, - showPresence: false, - floatingIndicator: Positioned( - bottom: 0, - right: 0, - child: Container( - width: 16, - height: 16, - decoration: BoxDecoration( - color: theme.colorScheme.surface, - borderRadius: BorderRadius.circular(32), - ), - alignment: Alignment.center, - child: Container( - width: 10, - height: 10, - decoration: BoxDecoration( - color: dotColor, - borderRadius: BorderRadius.circular(16), - border: Border.all( - width: 1, - color: theme.colorScheme.surface, - ), - ), - ), - ), - ), - ); - }, - ); - }, - ), - ), - ), - ); - }, - ); - }, - ); - } -} diff --git a/lib/pangea/spaces/utils/load_participants_util.dart b/lib/pangea/spaces/widgets/load_participants_builder.dart similarity index 100% rename from lib/pangea/spaces/utils/load_participants_util.dart rename to lib/pangea/spaces/widgets/load_participants_builder.dart diff --git a/lib/pangea/user/controllers/permissions_controller.dart b/lib/pangea/user/controllers/permissions_controller.dart index f00351043..d9ca47825 100644 --- a/lib/pangea/user/controllers/permissions_controller.dart +++ b/lib/pangea/user/controllers/permissions_controller.dart @@ -2,7 +2,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/pangea/common/controllers/base_controller.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; -import 'package:fluffychat/pangea/spaces/models/space_model.dart'; +import 'package:fluffychat/pangea/learning_settings/enums/tool_settings_enum.dart'; class PermissionsController extends BaseController { late PangeaController _pangeaController; diff --git a/lib/pangea/user/models/user_model.dart b/lib/pangea/user/models/user_model.dart index 6ffe2a844..6b89cdb1a 100644 --- a/lib/pangea/user/models/user_model.dart +++ b/lib/pangea/user/models/user_model.dart @@ -4,7 +4,7 @@ import 'package:fluffychat/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/instructions/instruction_settings.dart'; import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; -import 'package:fluffychat/pangea/spaces/models/space_model.dart'; +import 'package:fluffychat/pangea/learning_settings/enums/tool_settings_enum.dart'; import 'package:fluffychat/widgets/matrix.dart'; import '../../learning_settings/models/language_model.dart'; diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index 6044442eb..ec036889e 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -24,7 +24,7 @@ import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/any_state_holder.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/learning_settings/utils/locale_provider.dart'; -import 'package:fluffychat/pangea/spaces/controllers/space_code_controller.dart'; +import 'package:fluffychat/pangea/spaces/space_code_controller.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'; From 16fe7f28e36e3722e6ef2fe456f8b959689fa7d5 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 4 Dec 2025 09:12:48 -0500 Subject: [PATCH 6/9] move language settings getter into user controller --- lib/pages/chat/chat.dart | 13 +- lib/pages/chat/input_bar.dart | 7 +- lib/pages/chat_list/chat_list.dart | 1 - .../activity_generator.dart | 10 +- .../activity_generator_view.dart | 4 +- .../activity_chat_extension.dart | 4 +- .../activity_session_start_page.dart | 2 +- .../activity_sessions_start_view.dart | 11 +- .../activity_suggestions_area.dart | 4 +- .../analytics_details_popup.dart | 2 +- .../vocab_analytics_details_view.dart | 7 +- .../client_analytics_extension.dart | 2 +- .../get_analytics_controller.dart | 4 +- .../level_up/level_up_manager.dart | 2 +- .../level_up/level_up_popup.dart | 7 +- .../put_analytics_controller.dart | 10 +- .../learning_progress_indicators.dart | 4 +- .../chat/widgets/pangea_chat_input_row.dart | 4 +- .../utils/get_chat_list_item_subtitle.dart | 2 +- .../utils/bot_client_extension.dart | 4 +- lib/pangea/choreographer/choreographer.dart | 8 +- .../choreographer/igc/igc_controller.dart | 8 +- lib/pangea/choreographer/igc/span_card.dart | 3 +- .../choreographer/igc/span_data_model.dart | 2 +- .../igc/span_data_type_enum.dart | 2 +- lib/pangea/choreographer/it/it_bar.dart | 15 +-- .../choreographer/it/it_controller.dart | 6 +- .../choreographer/it/word_data_card.dart | 5 +- .../common/controllers/pangea_controller.dart | 33 ++---- .../constructs/construct_identifier.dart | 14 +-- .../course_topics/course_topic_model.dart | 4 +- .../courses/course_plan_builder.dart | 2 +- .../courses/course_plan_model.dart | 4 +- .../courses/course_plans_repo.dart | 2 +- .../event_wrappers/pangea_message_event.dart | 8 +- .../pangea_representation_event.dart | 4 +- .../models/representation_content_model.dart | 2 +- .../controllers/language_controller.dart | 112 ++---------------- .../enums/tool_settings_enum.dart | 2 +- .../pages/settings_learning.dart | 8 +- .../widgets/p_language_dialog.dart | 8 +- .../pages/create_pangea_account_page.dart | 8 +- .../login/pages/language_selection_page.dart | 5 +- lib/pangea/login/pages/new_course_page.dart | 2 +- .../login/pages/public_courses_page.dart | 4 +- .../token_emoji_button.dart | 3 +- .../message_token_text/tokens_util.dart | 2 +- .../morphs/morph_meaning/morph_info_repo.dart | 14 +-- lib/pangea/morphs/morph_repo.dart | 6 +- .../phonetic_transcription_widget.dart | 4 +- .../practice_selection_repo.dart | 4 +- .../download_space_analytics_dialog.dart | 2 +- .../space_analytics/space_analytics.dart | 2 +- .../spaces/widgets/join_with_link_page.dart | 4 +- lib/pangea/text_to_speech/tts_controller.dart | 10 +- .../token_info_feedback_dialog.dart | 4 +- .../practice_match_item.dart | 3 +- .../toolbar/widgets/overlay_message.dart | 4 +- .../practice_activity_card.dart | 2 +- .../toolbar/widgets/practice_controller.dart | 4 +- .../widgets/reading_assistance_content.dart | 3 +- .../widgets/select_mode_controller.dart | 18 +-- .../word_zoom/lemma_meaning_builder.dart | 5 +- .../widgets/word_zoom/word_zoom_widget.dart | 4 +- .../controllers/permissions_controller.dart | 78 ------------ .../user/controllers/user_controller.dart | 84 +++++++++++-- lib/pangea/word_bank/vocab_bank_repo.dart | 4 +- 67 files changed, 243 insertions(+), 396 deletions(-) delete mode 100644 lib/pangea/user/controllers/permissions_controller.dart diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index ef53d7696..bde9816cd 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -59,6 +59,7 @@ import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart' import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/instructions/instructions_enum.dart'; import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; +import 'package:fluffychat/pangea/learning_settings/controllers/language_controller.dart'; import 'package:fluffychat/pangea/learning_settings/repo/language_mismatch_repo.dart'; import 'package:fluffychat/pangea/learning_settings/widgets/p_language_dialog.dart'; import 'package:fluffychat/pangea/message_token_text/tokens_util.dart'; @@ -532,7 +533,7 @@ class ChatController extends State Future.delayed(const Duration(seconds: 1), () async { if (!mounted) return; - pangeaController.languageController.showDialogOnEmptyLanguage( + LanguageController.showDialogOnEmptyLanguage( context, () => () => setState(() {}), ); @@ -1153,8 +1154,8 @@ class ChatController extends State 'waveform': result.waveform, }, // #Pangea - 'speaker_l1': pangeaController.languageController.activeL1Code(), - 'speaker_l2': pangeaController.languageController.activeL2Code(), + 'speaker_l1': pangeaController.userController.userL1Code, + 'speaker_l2': pangeaController.userController.userL2Code, // Pangea# }, // #Pangea @@ -1990,7 +1991,7 @@ class ChatController extends State } // Check if the user has set their languages. If not, prompt them to do so. - if (!MatrixState.pangeaController.languageController.languagesSet) { + if (!MatrixState.pangeaController.userController.languagesSet) { pLanguageDialog(context, () {}); return; } @@ -2150,9 +2151,9 @@ class ChatController extends State ); final stt = await messageEvent.requestSpeechToText( - MatrixState.pangeaController.languageController.userL1?.langCodeShort ?? + MatrixState.pangeaController.userController.userL1?.langCodeShort ?? LanguageKeys.unknownLanguage, - MatrixState.pangeaController.languageController.userL2?.langCodeShort ?? + MatrixState.pangeaController.userController.userL2?.langCodeShort ?? LanguageKeys.unknownLanguage, ); if (stt.transcript.sttTokens.isEmpty) return; diff --git a/lib/pages/chat/input_bar.dart b/lib/pages/chat/input_bar.dart index ffeccc997..0e5f2f4fa 100644 --- a/lib/pages/chat/input_bar.dart +++ b/lib/pages/chat/input_bar.dart @@ -401,11 +401,10 @@ class InputBar extends StatelessWidget { MatrixState.pangeaController.subscriptionController.subscriptionStatus; String _defaultHintText(BuildContext context) { - final lang = MatrixState.pangeaController.languageController; - return lang.languagesSet + return MatrixState.pangeaController.userController.languagesSet ? L10n.of(context).writeAMessageLangCodes( - lang.userL1!.displayName, - lang.userL2!.displayName, + MatrixState.pangeaController.userController.userL1!.displayName, + MatrixState.pangeaController.userController.userL2!.displayName, ) : L10n.of(context).writeAMessage; } diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index 90d3bcfa1..fa1d779ca 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -42,7 +42,6 @@ import '../../widgets/matrix.dart'; import 'package:fluffychat/utils/tor_stub.dart' if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart'; - enum PopupMenuAction { settings, invite, diff --git a/lib/pangea/activity_generator/activity_generator.dart b/lib/pangea/activity_generator/activity_generator.dart index 4e1e237d9..6073211ff 100644 --- a/lib/pangea/activity_generator/activity_generator.dart +++ b/lib/pangea/activity_generator/activity_generator.dart @@ -59,9 +59,9 @@ // super.initState(); // selectedLanguageOfInstructions = -// MatrixState.pangeaController.languageController.userL1?.langCode; +// MatrixState.pangeaController.userController.userL1?.langCode; // selectedTargetLanguage = -// MatrixState.pangeaController.languageController.userL2?.langCode; +// MatrixState.pangeaController.userController.userL2?.langCode; // selectedCefrLevel = LanguageLevelTypeEnum.a1; // selectedNumberOfParticipants = 3; // _setMode(); @@ -79,7 +79,7 @@ // ActivitySettingRequestSchema get req => ActivitySettingRequestSchema( // langCode: -// MatrixState.pangeaController.languageController.userL1?.langCode ?? +// MatrixState.pangeaController.userController.userL1?.langCode ?? // LanguageKeys.defaultLanguage, // ); @@ -139,9 +139,9 @@ // modeController.clear(); // selectedMedia = MediaEnum.nan; // selectedLanguageOfInstructions = -// MatrixState.pangeaController.languageController.userL1?.langCode; +// MatrixState.pangeaController.userController.userL1?.langCode; // selectedTargetLanguage = -// MatrixState.pangeaController.languageController.userL2?.langCode; +// MatrixState.pangeaController.userController.userL2?.langCode; // selectedCefrLevel = LanguageLevelTypeEnum.a1; // selectedNumberOfParticipants = 3; // }); diff --git a/lib/pangea/activity_generator/activity_generator_view.dart b/lib/pangea/activity_generator/activity_generator_view.dart index 218c1c03b..b10b2101d 100644 --- a/lib/pangea/activity_generator/activity_generator_view.dart +++ b/lib/pangea/activity_generator/activity_generator_view.dart @@ -158,7 +158,7 @@ // controller.selectedLanguageOfInstructions!, // ) // : MatrixState -// .pangeaController.languageController.userL1, +// .pangeaController.userController.userL1, // isL2List: false, // decorationText: // L10n.of(context).languageOfInstructionsLabel, @@ -174,7 +174,7 @@ // controller.selectedTargetLanguage!, // ) // : MatrixState -// .pangeaController.languageController.userL2, +// .pangeaController.userController.userL2, // decorationText: L10n.of(context).targetLanguageLabel, // isL2List: true, // ), diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart index 77f3affda..1cf309eca 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart @@ -43,9 +43,9 @@ extension ActivityMenuLogic on ChatController { } final l1 = - MatrixState.pangeaController.languageController.userL1?.langCodeShort; + MatrixState.pangeaController.userController.userL1?.langCodeShort; final l2 = - MatrixState.pangeaController.languageController.userL2?.langCodeShort; + MatrixState.pangeaController.userController.userL2?.langCodeShort; final activityLang = room.activityPlan?.req.targetLanguage.split('-').first; return activityLang != null && diff --git a/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart b/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart index 7377eacaa..10d88ac12 100644 --- a/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart +++ b/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart @@ -309,7 +309,7 @@ class ActivitySessionStartController extends State final activitiesResponse = await CourseActivityRepo.get( TranslateActivityRequest( activityIds: [widget.activityId], - l1: MatrixState.pangeaController.languageController.activeL1Code()!, + l1: MatrixState.pangeaController.userController.userL1Code!, ), widget.activityId, ); diff --git a/lib/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart b/lib/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart index 044c06ee4..ea8a45e17 100644 --- a/lib/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart +++ b/lib/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart @@ -106,10 +106,10 @@ class ActivitySessionStartView extends StatelessWidget { activityId: controller.widget.activityId, feedbackText: feedback, userId: Matrix.of(context).client.userID!, - userL1: MatrixState.pangeaController.languageController - .activeL1Code()!, - userL2: MatrixState.pangeaController.languageController - .activeL2Code()!, + userL1: MatrixState + .pangeaController.userController.userL1Code!, + userL2: MatrixState + .pangeaController.userController.userL2Code!, ), ), ); @@ -120,8 +120,7 @@ class ActivitySessionStartView extends StatelessWidget { CourseActivityRepo.setSentFeedback( controller.widget.activityId, - MatrixState.pangeaController.languageController - .activeL1Code()!, + MatrixState.pangeaController.userController.userL1Code!, ); await showDialog( diff --git a/lib/pangea/activity_suggestions/activity_suggestions_area.dart b/lib/pangea/activity_suggestions/activity_suggestions_area.dart index 98952559a..da888eb65 100644 --- a/lib/pangea/activity_suggestions/activity_suggestions_area.dart +++ b/lib/pangea/activity_suggestions/activity_suggestions_area.dart @@ -75,10 +75,10 @@ // double get cardWidth => _isColumnMode ? 225.0 : 150.0; // String get instructionLanguage => -// MatrixState.pangeaController.languageController.userL1?.langCode ?? +// MatrixState.pangeaController.userController.userL1?.langCode ?? // LanguageKeys.defaultLanguage; // String get targetLanguage => -// MatrixState.pangeaController.languageController.userL2?.langCode ?? +// MatrixState.pangeaController.userController.userL2?.langCode ?? // LanguageKeys.defaultLanguage; // ActivityPlanRequest get _request { diff --git a/lib/pangea/analytics_details_popup/analytics_details_popup.dart b/lib/pangea/analytics_details_popup/analytics_details_popup.dart index 2ec51e746..baf2285a3 100644 --- a/lib/pangea/analytics_details_popup/analytics_details_popup.dart +++ b/lib/pangea/analytics_details_popup/analytics_details_popup.dart @@ -60,7 +60,7 @@ class ConstructAnalyticsViewState extends State { ErrorHandler.logError( e: e, s: s, - data: {"l2": MatrixState.pangeaController.languageController.userL2}, + data: {"l2": MatrixState.pangeaController.userController.userL2}, ); } finally { features.sort( diff --git a/lib/pangea/analytics_details_popup/vocab_analytics_details_view.dart b/lib/pangea/analytics_details_popup/vocab_analytics_details_view.dart index 1434c29f9..0f8b289c5 100644 --- a/lib/pangea/analytics_details_popup/vocab_analytics_details_view.dart +++ b/lib/pangea/analytics_details_popup/vocab_analytics_details_view.dart @@ -31,7 +31,7 @@ class VocabDetailsView extends StatelessWidget { /// Get the language code for the current lemma String? get _userL2 => - MatrixState.pangeaController.languageController.userL2?.langCode; + MatrixState.pangeaController.userController.userL2?.langCode; List get forms => MatrixState.pangeaController.getAnalytics.constructListModel @@ -69,14 +69,13 @@ class VocabDetailsView extends StatelessWidget { ); }, ), - if (MatrixState - .pangeaController.languageController.showTranscription) + if (MatrixState.pangeaController.userController.showTranscription) Padding( padding: const EdgeInsets.only(top: 4.0), child: PhoneticTranscriptionWidget( text: _construct.lemma, textLanguage: - MatrixState.pangeaController.languageController.userL2!, + MatrixState.pangeaController.userController.userL2!, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: textColor.withAlpha((0.7 * 255).toInt()), fontSize: 18, diff --git a/lib/pangea/analytics_misc/client_analytics_extension.dart b/lib/pangea/analytics_misc/client_analytics_extension.dart index ca92ff2cc..0a8f9d431 100644 --- a/lib/pangea/analytics_misc/client_analytics_extension.dart +++ b/lib/pangea/analytics_misc/client_analytics_extension.dart @@ -27,7 +27,7 @@ extension AnalyticsClientExtension on Client { /// optional userId (if not specified, uses current user). /// If user is invited to the room, joins the room. Room? analyticsRoomLocal([LanguageModel? lang, String? userIdParam]) { - lang ??= MatrixState.pangeaController.languageController.userL2; + lang ??= MatrixState.pangeaController.userController.userL2; if (lang == null) { debugger(when: kDebugMode); diff --git a/lib/pangea/analytics_misc/get_analytics_controller.dart b/lib/pangea/analytics_misc/get_analytics_controller.dart index 726826225..d12b0cc18 100644 --- a/lib/pangea/analytics_misc/get_analytics_controller.dart +++ b/lib/pangea/analytics_misc/get_analytics_controller.dart @@ -45,8 +45,8 @@ class GetAnalyticsController extends BaseController { _pangeaController = pangeaController; } - LanguageModel? get _l1 => _pangeaController.languageController.userL1; - LanguageModel? get _l2 => _pangeaController.languageController.userL2; + LanguageModel? get _l1 => _pangeaController.userController.userL1; + LanguageModel? get _l2 => _pangeaController.userController.userL2; Client get _client => _pangeaController.matrixState.client; diff --git a/lib/pangea/analytics_misc/level_up/level_up_manager.dart b/lib/pangea/analytics_misc/level_up/level_up_manager.dart index 0d1ada254..3e8a7e5f3 100644 --- a/lib/pangea/analytics_misc/level_up/level_up_manager.dart +++ b/lib/pangea/analytics_misc/level_up/level_up_manager.dart @@ -43,7 +43,7 @@ class LevelUpManager { .numConstructs(ConstructTypeEnum.vocab); final LanguageModel? l2 = - MatrixState.pangeaController.languageController.userL2; + MatrixState.pangeaController.userController.userL2; final Room? analyticsRoom = MatrixState.pangeaController.matrixState.client.analyticsRoomLocal(l2!); diff --git a/lib/pangea/analytics_misc/level_up/level_up_popup.dart b/lib/pangea/analytics_misc/level_up/level_up_popup.dart index 5d9f7192e..b70506f81 100644 --- a/lib/pangea/analytics_misc/level_up/level_up_popup.dart +++ b/lib/pangea/analytics_misc/level_up/level_up_popup.dart @@ -107,10 +107,9 @@ class _LevelUpPopupContentState extends State Uri? avatarUrl; final bool _hasBlastedConfetti = false; - String language = MatrixState.pangeaController.languageController - .activeL2Code() - ?.toUpperCase() ?? - LanguageKeys.unknownLanguage; + String language = + MatrixState.pangeaController.userController.userL2Code?.toUpperCase() ?? + LanguageKeys.unknownLanguage; ConstructSummary? _constructSummary; Object? _error; diff --git a/lib/pangea/analytics_misc/put_analytics_controller.dart b/lib/pangea/analytics_misc/put_analytics_controller.dart index eb1d9006c..9548f1774 100644 --- a/lib/pangea/analytics_misc/put_analytics_controller.dart +++ b/lib/pangea/analytics_misc/put_analytics_controller.dart @@ -404,7 +404,7 @@ class PutAnalyticsController extends BaseController { if (cachedConstructs.isEmpty || onlyDraft) return; // if missing important info, don't send analytics. Could happen if user just signed up. - final l2 = l2Override ?? _pangeaController.languageController.userL2; + final l2 = l2Override ?? _pangeaController.userController.userL2; if (l2 == null || _client.userID == null) return; // analytics room for the user and current target language @@ -418,10 +418,10 @@ class PutAnalyticsController extends BaseController { Future sendActivityAnalytics(String roomId) async { if (_pangeaController.matrixState.client.userID == null) return; - if (_pangeaController.languageController.userL2 == null) return; + if (_pangeaController.userController.userL2 == null) return; final Room? analyticsRoom = await _client.getMyAnalyticsRoom( - _pangeaController.languageController.userL2!, + _pangeaController.userController.userL2!, ); if (analyticsRoom == null) return; await analyticsRoom.addActivityRoomId(roomId); @@ -436,10 +436,10 @@ class PutAnalyticsController extends BaseController { Future removeActivityAnalytics(String roomId) async { if (_pangeaController.matrixState.client.userID == null) return; - if (_pangeaController.languageController.userL2 == null) return; + if (_pangeaController.userController.userL2 == null) return; final Room? analyticsRoom = await _client.getMyAnalyticsRoom( - _pangeaController.languageController.userL2!, + _pangeaController.userController.userL2!, ); if (analyticsRoom == null) return; await analyticsRoom.removeActivityRoomId(roomId); diff --git a/lib/pangea/analytics_summary/learning_progress_indicators.dart b/lib/pangea/analytics_summary/learning_progress_indicators.dart index e75c03d35..b46e4d000 100644 --- a/lib/pangea/analytics_summary/learning_progress_indicators.dart +++ b/lib/pangea/analytics_summary/learning_progress_indicators.dart @@ -97,8 +97,8 @@ class LearningProgressIndicatorsState return const SizedBox(); } - final userL1 = MatrixState.pangeaController.languageController.userL1; - final userL2 = MatrixState.pangeaController.languageController.userL2; + final userL1 = MatrixState.pangeaController.userController.userL1; + final userL2 = MatrixState.pangeaController.userController.userL2; final isColumnMode = FluffyThemes.isColumnMode(context); diff --git a/lib/pangea/chat/widgets/pangea_chat_input_row.dart b/lib/pangea/chat/widgets/pangea_chat_input_row.dart index 36261bee6..eed64f97a 100644 --- a/lib/pangea/chat/widgets/pangea_chat_input_row.dart +++ b/lib/pangea/chat/widgets/pangea_chat_input_row.dart @@ -24,9 +24,9 @@ class PangeaChatInputRow extends StatelessWidget { }); LanguageModel? get activel1 => - controller.pangeaController.languageController.activeL1Model(); + controller.pangeaController.userController.userL1; LanguageModel? get activel2 => - controller.pangeaController.languageController.activeL2Model(); + controller.pangeaController.userController.userL2; @override Widget build(BuildContext context) { diff --git a/lib/pangea/chat_list/utils/get_chat_list_item_subtitle.dart b/lib/pangea/chat_list/utils/get_chat_list_item_subtitle.dart index c4798e637..3b86069b6 100644 --- a/lib/pangea/chat_list/utils/get_chat_list_item_subtitle.dart +++ b/lib/pangea/chat_list/utils/get_chat_list_item_subtitle.dart @@ -21,7 +21,7 @@ class ChatListItemSubtitle extends StatelessWidget { }); bool _showPangeaContent(Event event) { - return MatrixState.pangeaController.languageController.languagesSet && + return MatrixState.pangeaController.userController.languagesSet && !event.redacted && event.type == EventTypes.Message && event.messageType == MessageTypes.Text && diff --git a/lib/pangea/chat_settings/utils/bot_client_extension.dart b/lib/pangea/chat_settings/utils/bot_client_extension.dart index 23f39b036..407f40f6b 100644 --- a/lib/pangea/chat_settings/utils/bot_client_extension.dart +++ b/lib/pangea/chat_settings/utils/bot_client_extension.dart @@ -41,8 +41,8 @@ extension BotClientExtension on Client { StateEvent( content: BotOptionsModel( mode: BotMode.directChat, - targetLanguage: MatrixState - .pangeaController.languageController.userL2?.langCode, + targetLanguage: + MatrixState.pangeaController.userController.userL2?.langCode, languageLevel: MatrixState.pangeaController.userController.profile .userSettings.cefrLevel, ).toJson(), diff --git a/lib/pangea/choreographer/choreographer.dart b/lib/pangea/choreographer/choreographer.dart index c98ae1c2b..f7fd0e9c3 100644 --- a/lib/pangea/choreographer/choreographer.dart +++ b/lib/pangea/choreographer/choreographer.dart @@ -223,8 +223,8 @@ class Choreographer extends ChangeNotifier { MatrixState.pangeaController.subscriptionController.subscriptionStatus; if (canSendStatus != SubscriptionStatus.subscribed || - MatrixState.pangeaController.languageController.userL2 == null || - MatrixState.pangeaController.languageController.userL1 == null || + MatrixState.pangeaController.userController.userL2 == null || + MatrixState.pangeaController.userController.userL1 == null || (!ToolSetting.interactiveGrammar.enabled && !ToolSetting.interactiveTranslator.enabled) || (!ToolSetting.autoIGC.enabled && @@ -261,9 +261,9 @@ class Choreographer extends ChangeNotifier { Future getMessageContent(String message) async { TokensResponseModel? tokensResp; final l2LangCode = - MatrixState.pangeaController.languageController.userL2?.langCode; + MatrixState.pangeaController.userController.userL2?.langCode; final l1LangCode = - MatrixState.pangeaController.languageController.userL1?.langCode; + MatrixState.pangeaController.userController.userL1?.langCode; if (l1LangCode != null && l2LangCode != null) { final res = await TokensRepo.get( MatrixState.pangeaController.userController.accessToken, diff --git a/lib/pangea/choreographer/igc/igc_controller.dart b/lib/pangea/choreographer/igc/igc_controller.dart index 91766390b..68db0d782 100644 --- a/lib/pangea/choreographer/igc/igc_controller.dart +++ b/lib/pangea/choreographer/igc/igc_controller.dart @@ -69,16 +69,16 @@ class IgcController { IGCRequestModel( fullText: text, userId: MatrixState.pangeaController.userController.userId!, - userL1: MatrixState.pangeaController.languageController.activeL1Code()!, - userL2: MatrixState.pangeaController.languageController.activeL2Code()!, + userL1: MatrixState.pangeaController.userController.userL1Code!, + userL2: MatrixState.pangeaController.userController.userL2Code!, enableIGC: true, enableIT: true, prevMessages: prevMessages, ); SpanDetailsRequest _spanDetailsRequest(SpanData span) => SpanDetailsRequest( - userL1: MatrixState.pangeaController.languageController.activeL1Code()!, - userL2: MatrixState.pangeaController.languageController.activeL2Code()!, + userL1: MatrixState.pangeaController.userController.userL1Code!, + userL2: MatrixState.pangeaController.userController.userL2Code!, enableIGC: true, enableIT: true, span: span, diff --git a/lib/pangea/choreographer/igc/span_card.dart b/lib/pangea/choreographer/igc/span_card.dart index 0dfb84374..4367766d0 100644 --- a/lib/pangea/choreographer/igc/span_card.dart +++ b/lib/pangea/choreographer/igc/span_card.dart @@ -177,8 +177,7 @@ class SpanCardState extends State { widget.match.updatedMatch.match.selectedChoiceIndex, id: widget.match.hashCode.toString(), langCode: MatrixState - .pangeaController.languageController - .activeL2Code(), + .pangeaController.userController.userL2Code!, ), _SpanCardFeedback( _selectedChoice != null, diff --git a/lib/pangea/choreographer/igc/span_data_model.dart b/lib/pangea/choreographer/igc/span_data_model.dart index f4d01f142..3ab2e3ea7 100644 --- a/lib/pangea/choreographer/igc/span_data_model.dart +++ b/lib/pangea/choreographer/igc/span_data_model.dart @@ -128,7 +128,7 @@ class SpanData { final errorSpan = fullText.characters.skip(offset).take(length).toString(); final l2Code = - MatrixState.pangeaController.languageController.userL2?.langCodeShort; + MatrixState.pangeaController.userController.userL2?.langCodeShort; return correctChoice != null && l2Code != null && diff --git a/lib/pangea/choreographer/igc/span_data_type_enum.dart b/lib/pangea/choreographer/igc/span_data_type_enum.dart index c2481da0e..d3cfa4b87 100644 --- a/lib/pangea/choreographer/igc/span_data_type_enum.dart +++ b/lib/pangea/choreographer/igc/span_data_type_enum.dart @@ -34,7 +34,7 @@ extension SpanDataTypeEnumExt on SpanDataTypeEnum { return L10n.of(context).correctionDefaultPrompt; case SpanDataTypeEnum.itStart: return L10n.of(context).needsItMessage( - MatrixState.pangeaController.languageController.userL2 + MatrixState.pangeaController.userController.userL2 ?.getDisplayName(context) ?? L10n.of(context).targetLanguage, ); diff --git a/lib/pangea/choreographer/it/it_bar.dart b/lib/pangea/choreographer/it/it_bar.dart index dfdd6e87a..261b97872 100644 --- a/lib/pangea/choreographer/it/it_bar.dart +++ b/lib/pangea/choreographer/it/it_bar.dart @@ -62,12 +62,9 @@ class ITBarState extends State with SingleTickerProviderStateMixin { FullTextTranslationRequestModel _translationRequest(String text) => FullTextTranslationRequestModel( text: text, - tgtLang: - MatrixState.pangeaController.languageController.userL1!.langCode, - userL1: - MatrixState.pangeaController.languageController.userL1!.langCode, - userL2: - MatrixState.pangeaController.languageController.userL2!.langCode, + tgtLang: MatrixState.pangeaController.userController.userL1!.langCode, + userL1: MatrixState.pangeaController.userController.userL1!.langCode, + userL2: MatrixState.pangeaController.userController.userL2!.langCode, ); void _openListener() { @@ -105,8 +102,8 @@ class ITBarState extends State with SingleTickerProviderStateMixin { cardToShow: selected ? WordDataCard( word: text, - langCode: MatrixState - .pangeaController.languageController.userL2!.langCode, + langCode: + MatrixState.pangeaController.userController.userL2!.langCode, fullText: _sourceText.value ?? widget.choreographer.currentText, ) : ITFeedbackCard(_translationRequest(text)), @@ -394,7 +391,7 @@ class _ITChoices extends StatelessWidget { onPressed: (value, index) => onPressed(index), onLongPress: (value, index) => onLongPressed(continuances[index]), selectedChoiceIndex: null, - langCode: MatrixState.pangeaController.languageController.activeL2Code(), + langCode: MatrixState.pangeaController.userController.userL2Code!, ); } } diff --git a/lib/pangea/choreographer/it/it_controller.dart b/lib/pangea/choreographer/it/it_controller.dart index af2959df3..fef1bb239 100644 --- a/lib/pangea/choreographer/it/it_controller.dart +++ b/lib/pangea/choreographer/it/it_controller.dart @@ -41,10 +41,8 @@ class ITController { return ITRequestModel( text: _sourceText.value!, customInput: textInput, - sourceLangCode: - MatrixState.pangeaController.languageController.activeL1Code()!, - targetLangCode: - MatrixState.pangeaController.languageController.activeL2Code()!, + sourceLangCode: MatrixState.pangeaController.userController.userL1Code!, + targetLangCode: MatrixState.pangeaController.userController.userL2Code!, goldTranslation: _goldRouteTracker?.fullTranslation, goldContinuances: _goldRouteTracker?.continuances, ); diff --git a/lib/pangea/choreographer/it/word_data_card.dart b/lib/pangea/choreographer/it/word_data_card.dart index c0dae4dcf..b99ea0871 100644 --- a/lib/pangea/choreographer/it/word_data_card.dart +++ b/lib/pangea/choreographer/it/word_data_card.dart @@ -29,9 +29,8 @@ class WordDataCard extends StatelessWidget { word: word, fullTextLang: langCode, wordLang: langCode, - feedbackLang: - MatrixState.pangeaController.languageController.activeL1Code() ?? - LanguageKeys.defaultLanguage, + feedbackLang: MatrixState.pangeaController.userController.userL1Code ?? + LanguageKeys.defaultLanguage, ); Future> _fetchDefinition() { diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index 5f3636abb..2c3de2d4c 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:math'; import 'package:flutter/material.dart'; @@ -17,12 +16,10 @@ import 'package:fluffychat/pangea/chat_settings/utils/bot_client_extension.dart' import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/guard/p_vguard.dart'; -import 'package:fluffychat/pangea/learning_settings/controllers/language_controller.dart'; import 'package:fluffychat/pangea/learning_settings/utils/locale_provider.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart'; import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; -import 'package:fluffychat/pangea/user/controllers/permissions_controller.dart'; import 'package:fluffychat/pangea/user/controllers/user_controller.dart'; import 'package:fluffychat/widgets/matrix.dart'; import '../utils/firebase_analytics.dart'; @@ -30,8 +27,6 @@ import '../utils/firebase_analytics.dart'; class PangeaController { ///pangeaControllers late UserController userController; - late LanguageController languageController; - late PermissionsController permissionsController; late GetAnalyticsController getAnalytics; late PutAnalyticsController putAnalytics; late SubscriptionController subscriptionController; @@ -46,16 +41,13 @@ class PangeaController { MatrixState matrixState; Matrix matrix; - int? randomint; PangeaController({required this.matrix, required this.matrixState}) { - _setup(); + userController = UserController(this); + getAnalytics = GetAnalyticsController(this); + putAnalytics = PutAnalyticsController(this); + subscriptionController = SubscriptionController(this); + PAuthGaurd.pController = this; _setSettingsSubscriptions(); - randomint = Random().nextInt(2000); - } - - /// Pangea Initialization - void _setup() { - _addRefInObjects(); } /// Initializes various controllers and settings. @@ -69,18 +61,7 @@ class PangeaController { TtsController.setAvailableLanguages(); } - /// Initialize controllers - _addRefInObjects() { - userController = UserController(this); - languageController = LanguageController(this); - permissionsController = PermissionsController(this); - getAnalytics = GetAnalyticsController(this); - putAnalytics = PutAnalyticsController(this); - subscriptionController = SubscriptionController(this); - PAuthGaurd.pController = this; - } - - _logOutfromPangea(BuildContext context) { + void _logOutfromPangea(BuildContext context) { debugPrint("Pangea logout"); GoogleAnalytics.logout(); clearCache(); @@ -243,7 +224,7 @@ class PangeaController { return; } - final targetLanguage = languageController.userL2?.langCode; + final targetLanguage = userController.userL2?.langCode; final cefrLevel = userController.profile.userSettings.cefrLevel; final updateBotOptions = botDM.botOptions ?? BotOptionsModel(); diff --git a/lib/pangea/constructs/construct_identifier.dart b/lib/pangea/constructs/construct_identifier.dart index c23d821bc..8ceb09234 100644 --- a/lib/pangea/constructs/construct_identifier.dart +++ b/lib/pangea/constructs/construct_identifier.dart @@ -152,12 +152,12 @@ class ConstructIdentifier { LemmaInfoRequest get _lemmaInfoRequest => LemmaInfoRequest( partOfSpeech: category, - lemmaLang: MatrixState - .pangeaController.languageController.userL2?.langCodeShort ?? - LanguageKeys.defaultLanguage, - userL1: MatrixState - .pangeaController.languageController.userL1?.langCodeShort ?? - LanguageKeys.defaultLanguage, + lemmaLang: + MatrixState.pangeaController.userController.userL2?.langCodeShort ?? + LanguageKeys.defaultLanguage, + userL1: + MatrixState.pangeaController.userController.userL1?.langCodeShort ?? + LanguageKeys.defaultLanguage, lemma: lemma, ); @@ -187,7 +187,7 @@ class ConstructIdentifier { Future setUserLemmaInfo(UserSetLemmaInfo newLemmaInfo) async { final client = MatrixState.pangeaController.matrixState.client; - final l2 = MatrixState.pangeaController.languageController.userL2; + final l2 = MatrixState.pangeaController.userController.userL2; if (l2 == null) return; final analyticsRoom = await client.getMyAnalyticsRoom(l2); diff --git a/lib/pangea/course_plans/course_topics/course_topic_model.dart b/lib/pangea/course_plans/course_topics/course_topic_model.dart index b48c8d923..36e1404d1 100644 --- a/lib/pangea/course_plans/course_topics/course_topic_model.dart +++ b/lib/pangea/course_plans/course_topics/course_topic_model.dart @@ -90,7 +90,7 @@ class CourseTopicModel { CourseActivityRepo.getCached( TranslateActivityRequest( activityIds: activityIds, - l1: MatrixState.pangeaController.languageController.activeL1Code()!, + l1: MatrixState.pangeaController.userController.userL1Code!, ), ).plans; @@ -98,7 +98,7 @@ class CourseTopicModel { final resp = await CourseActivityRepo.get( TranslateActivityRequest( activityIds: activityIds, - l1: MatrixState.pangeaController.languageController.activeL1Code()!, + l1: MatrixState.pangeaController.userController.userL1Code!, ), uuid, ); diff --git a/lib/pangea/course_plans/courses/course_plan_builder.dart b/lib/pangea/course_plans/courses/course_plan_builder.dart index 9f88c7faa..93fbe0118 100644 --- a/lib/pangea/course_plans/courses/course_plan_builder.dart +++ b/lib/pangea/course_plans/courses/course_plan_builder.dart @@ -43,7 +43,7 @@ mixin CoursePlanProvider on State { course = await CoursePlansRepo.get( GetLocalizedCoursesRequest( coursePlanIds: [courseId], - l1: MatrixState.pangeaController.languageController.activeL1Code()!, + l1: MatrixState.pangeaController.userController.userL1Code!, ), ); } catch (e) { diff --git a/lib/pangea/course_plans/courses/course_plan_model.dart b/lib/pangea/course_plans/courses/course_plan_model.dart index 2f52a44d6..57e47387a 100644 --- a/lib/pangea/course_plans/courses/course_plan_model.dart +++ b/lib/pangea/course_plans/courses/course_plan_model.dart @@ -95,7 +95,7 @@ class CoursePlanModel { Map get loadedTopics => CourseTopicRepo.getCached( TranslateTopicRequest( topicIds: topicIds, - l1: MatrixState.pangeaController.languageController.activeL1Code()!, + l1: MatrixState.pangeaController.userController.userL1Code!, ), ).topics; @@ -106,7 +106,7 @@ class CoursePlanModel { final resp = await CourseTopicRepo.get( TranslateTopicRequest( topicIds: topicIds, - l1: MatrixState.pangeaController.languageController.activeL1Code()!, + l1: MatrixState.pangeaController.userController.userL1Code!, ), uuid, ); diff --git a/lib/pangea/course_plans/courses/course_plans_repo.dart b/lib/pangea/course_plans/courses/course_plans_repo.dart index 36011076a..0106046b3 100644 --- a/lib/pangea/course_plans/courses/course_plans_repo.dart +++ b/lib/pangea/course_plans/courses/course_plans_repo.dart @@ -151,7 +151,7 @@ class CoursePlansRepo { return search( GetLocalizedCoursesRequest( coursePlanIds: result.docs, - l1: MatrixState.pangeaController.languageController.activeL1Code()!, + l1: MatrixState.pangeaController.userController.userL1Code!, ), ); } diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index d0568cdc2..23e49440e 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -75,11 +75,10 @@ class PangeaMessageEvent { bool get isAudioMessage => _event.messageType == MessageTypes.Audio; - String? get _l2Code => - MatrixState.pangeaController.languageController.activeL2Code(); + String? get _l2Code => MatrixState.pangeaController.userController.userL2Code; String? get _l1Code => - MatrixState.pangeaController.languageController.userL1?.langCode; + MatrixState.pangeaController.userController.userL1?.langCode; Event? _latestEditCache; Event get _latestEdit => _latestEditCache ??= _event @@ -255,8 +254,7 @@ class PangeaMessageEvent { return stt.langCode; } - final bool immersionMode = MatrixState - .pangeaController.permissionsController + final bool immersionMode = MatrixState.pangeaController.userController .isToolEnabled(ToolSetting.immersionMode); final String? originalLangCode = originalSent?.langCode; diff --git a/lib/pangea/events/event_wrappers/pangea_representation_event.dart b/lib/pangea/events/event_wrappers/pangea_representation_event.dart index 14c4ab0e9..d87ab9444 100644 --- a/lib/pangea/events/event_wrappers/pangea_representation_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_representation_event.dart @@ -228,10 +228,10 @@ class RepresentationEvent { fullText: text, langCode: langCode, senderL1: - MatrixState.pangeaController.languageController.userL1?.langCode ?? + MatrixState.pangeaController.userController.userL1?.langCode ?? LanguageKeys.unknownLanguage, senderL2: - MatrixState.pangeaController.languageController.userL2?.langCode ?? + MatrixState.pangeaController.userController.userL2?.langCode ?? LanguageKeys.unknownLanguage, ), ); diff --git a/lib/pangea/events/models/representation_content_model.dart b/lib/pangea/events/models/representation_content_model.dart index 9bf6b0c97..1f4d3d45d 100644 --- a/lib/pangea/events/models/representation_content_model.dart +++ b/lib/pangea/events/models/representation_content_model.dart @@ -94,7 +94,7 @@ class PangeaRepresentation { bool get langCodeMatchesL2 => langCode.split("-").first == - MatrixState.pangeaController.languageController.userL2?.langCodeShort; + MatrixState.pangeaController.userController.userL2?.langCodeShort; /// Get construct uses for the message that weren't captured during language assistance. /// Takes a list of tokens and a choreo record, which is searched diff --git a/lib/pangea/learning_settings/controllers/language_controller.dart b/lib/pangea/learning_settings/controllers/language_controller.dart index e76270af6..f9d57b8e3 100644 --- a/lib/pangea/learning_settings/controllers/language_controller.dart +++ b/lib/pangea/learning_settings/controllers/language_controller.dart @@ -4,122 +4,24 @@ import 'package:flutter/material.dart'; import 'package:universal_io/io.dart'; -import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; +import 'package:fluffychat/widgets/matrix.dart'; import '../widgets/p_language_dialog.dart'; class LanguageController { - late PangeaController _pangeaController; - - LanguageController(PangeaController pangeaController) { - _pangeaController = pangeaController; - } - //show diloag when user does not have languages selected - showDialogOnEmptyLanguage(BuildContext context, Function callback) { - if (!languagesSet) { + static void showDialogOnEmptyLanguage( + BuildContext context, + Function callback, + ) { + if (!MatrixState.pangeaController.userController.languagesSet) { pLanguageDialog(context, callback); } } - bool get languagesSet => - _userL1Code != null && - _userL2Code != null && - _userL1Code!.isNotEmpty && - _userL2Code!.isNotEmpty && - _userL1Code != LanguageKeys.unknownLanguage && - _userL2Code != LanguageKeys.unknownLanguage; - - LanguageModel? get systemLanguage { + static LanguageModel? get systemLanguage { if (Platform.localeName.length < 2) return null; final String systemLang = Platform.localeName.substring(0, 2); return PLanguageStore.byLangCode(systemLang); } - - String? get _userL1Code { - final source = - _pangeaController.userController.profile.userSettings.sourceLanguage; - return source == null || source.isEmpty ? systemLanguage?.langCode : source; - } - - String? get _userL2Code { - final target = - _pangeaController.userController.profile.userSettings.targetLanguage; - return target == null || target.isEmpty ? null : target; - } - - LanguageModel? get userL1 { - if (_userL1Code == null) return null; - final langModel = PLanguageStore.byLangCode(_userL1Code!); - return langModel?.langCode == LanguageKeys.unknownLanguage - ? null - : langModel; - } - - LanguageModel? get userL2 { - if (_userL2Code == null) return null; - final langModel = PLanguageStore.byLangCode(_userL2Code!); - return langModel?.langCode == LanguageKeys.unknownLanguage - ? null - : langModel; - } - - String? activeL1Code() { - return _userL1Code; - // final String? activeL2 = activeL2Code(roomID: roomID); - // if (roomID == null || activeL2 != _userL1Code) { - // return _userL1Code; - // } - // final LanguageSettingsModel? classContext = _pangeaController - // .matrixState.client - // .getRoomById(roomID) - // ?.firstLanguageSettings; - // final String? classL1 = classContext?.dominantLanguage; - // if (classL1 == LanguageKeys.mixedLanguage || - // classL1 == LanguageKeys.multiLanguage || - // classL1 == null) { - // if (_userL2Code != _userL1Code) { - // return _userL2Code; - // } - // return LanguageKeys.unknownLanguage; - // } - // return classL1; - } - - /// Class languages override user languages within a class context - String? activeL2Code() { - return _userL2Code; - // if (roomID == null) { - // return _userL2Code; - // } - // final LanguageSettingsModel? classContext = _pangeaController - // .matrixState.client - // .getRoomById(roomID) - // ?.firstLanguageSettings; - // return classContext?.targetLanguage ?? _userL2Code; - } - - LanguageModel? activeL1Model() { - return userL1; - // final activeL1 = activeL1Code(roomID: roomID); - // return activeL1 != null ? PangeaLanguage.byLangCode(activeL1) : null; - } - - LanguageModel? activeL2Model() { - return userL2; - // final activeL2 = activeL2Code(roomID: roomID); - // final model = activeL2 != null ? PangeaLanguage.byLangCode(activeL2) : null; - // return model; - } - - bool get showTranscription => - (_pangeaController.languageController.userL1 != null && - _pangeaController.languageController.userL2 != null && - _pangeaController.languageController.userL1?.script != - _pangeaController.languageController.userL2?.script) || - (_pangeaController.languageController.userL1?.script != - LanguageKeys.unknownLanguage || - _pangeaController.languageController.userL2?.script == - LanguageKeys.unknownLanguage); } diff --git a/lib/pangea/learning_settings/enums/tool_settings_enum.dart b/lib/pangea/learning_settings/enums/tool_settings_enum.dart index 3533db831..a2d7f56e6 100644 --- a/lib/pangea/learning_settings/enums/tool_settings_enum.dart +++ b/lib/pangea/learning_settings/enums/tool_settings_enum.dart @@ -66,5 +66,5 @@ enum ToolSetting { } bool get enabled => - MatrixState.pangeaController.permissionsController.isToolEnabled(this); + MatrixState.pangeaController.userController.isToolEnabled(this); } diff --git a/lib/pangea/learning_settings/pages/settings_learning.dart b/lib/pangea/learning_settings/pages/settings_learning.dart index 5225bbc7b..58bfde9f2 100644 --- a/lib/pangea/learning_settings/pages/settings_learning.dart +++ b/lib/pangea/learning_settings/pages/settings_learning.dart @@ -9,6 +9,7 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/instructions/instruction_settings.dart'; +import 'package:fluffychat/pangea/learning_settings/controllers/language_controller.dart'; import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; import 'package:fluffychat/pangea/learning_settings/enums/tool_settings_enum.dart'; import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; @@ -234,8 +235,7 @@ class SettingsLearningController extends State { _profile.userSettings.targetLanguage != null && _targetLanguage != null; LanguageModel? get selectedSourceLanguage { - return _selectedBaseLanguage ?? - pangeaController.languageController.systemLanguage; + return _selectedBaseLanguage ?? LanguageController.systemLanguage; } LanguageModel? get selectedTargetLanguage { @@ -255,8 +255,8 @@ class SettingsLearningController extends State { ? PLanguageStore.byLangCode(_profile.userSettings.targetLanguage!) : null; - LanguageModel? get userL1 => pangeaController.languageController.userL1; - LanguageModel? get userL2 => pangeaController.languageController.userL2; + LanguageModel? get userL1 => pangeaController.userController.userL1; + LanguageModel? get userL2 => pangeaController.userController.userL2; bool get publicProfile => _profile.userSettings.publicProfile ?? false; diff --git a/lib/pangea/learning_settings/widgets/p_language_dialog.dart b/lib/pangea/learning_settings/widgets/p_language_dialog.dart index 92dce18a8..49598c587 100644 --- a/lib/pangea/learning_settings/widgets/p_language_dialog.dart +++ b/lib/pangea/learning_settings/widgets/p_language_dialog.dart @@ -7,6 +7,7 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; +import 'package:fluffychat/pangea/learning_settings/controllers/language_controller.dart'; import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; @@ -21,10 +22,9 @@ Future pLanguageDialog( ) async { final PangeaController pangeaController = MatrixState.pangeaController; //PTODO: if source language not set by user, default to languge from device settings - final LanguageModel? userL1 = pangeaController.languageController.userL1; - final LanguageModel? userL2 = pangeaController.languageController.userL2; - final LanguageModel? systemLanguage = - pangeaController.languageController.systemLanguage; + final LanguageModel? userL1 = pangeaController.userController.userL1; + final LanguageModel? userL2 = pangeaController.userController.userL2; + final LanguageModel? systemLanguage = LanguageController.systemLanguage; LanguageModel? selectedSourceLanguage = systemLanguage; if (userL1 != null && userL1.langCode != LanguageKeys.unknownLanguage) { diff --git a/lib/pangea/login/pages/create_pangea_account_page.dart b/lib/pangea/login/pages/create_pangea_account_page.dart index 73a9da82e..2cb04d403 100644 --- a/lib/pangea/login/pages/create_pangea_account_page.dart +++ b/lib/pangea/login/pages/create_pangea_account_page.dart @@ -13,6 +13,7 @@ import 'package:fluffychat/pangea/common/widgets/error_indicator.dart'; import 'package:fluffychat/pangea/course_plans/courses/course_plan_room_extension.dart'; import 'package:fluffychat/pangea/course_plans/courses/course_plans_repo.dart'; import 'package:fluffychat/pangea/course_plans/courses/get_localized_courses_request.dart'; +import 'package:fluffychat/pangea/learning_settings/controllers/language_controller.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; import 'package:fluffychat/pangea/login/utils/lang_code_repo.dart'; import 'package:fluffychat/pangea/spaces/space_code_controller.dart'; @@ -52,7 +53,7 @@ class CreatePangeaAccountPageState extends State { Future get _baseLangCode async => (await _cachedLangCode)?.baseLangCode ?? - MatrixState.pangeaController.languageController.systemLanguage?.langCode; + LanguageController.systemLanguage?.langCode; String? get _cachedSpaceCode => SpaceCodeRepo.spaceCode; @@ -99,7 +100,7 @@ class CreatePangeaAccountPageState extends State { final course = await CoursePlansRepo.get( GetLocalizedCoursesRequest( coursePlanIds: [courseId], - l1: MatrixState.pangeaController.languageController.activeL1Code()!, + l1: MatrixState.pangeaController.userController.userL1Code!, ), ); @@ -187,8 +188,7 @@ class CreatePangeaAccountPageState extends State { if (targetLangCode != null) MatrixState.pangeaController.userController.updateAnalyticsProfile( targetLanguage: PLanguageStore.byLangCode(targetLangCode), - baseLanguage: - MatrixState.pangeaController.languageController.systemLanguage, + baseLanguage: LanguageController.systemLanguage, level: 1, ), ]; diff --git a/lib/pangea/login/pages/language_selection_page.dart b/lib/pangea/login/pages/language_selection_page.dart index 5fba78707..652d9e86c 100644 --- a/lib/pangea/login/pages/language_selection_page.dart +++ b/lib/pangea/login/pages/language_selection_page.dart @@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; +import 'package:fluffychat/pangea/learning_settings/controllers/language_controller.dart'; import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; import 'package:fluffychat/pangea/learning_settings/widgets/p_language_dropdown.dart'; @@ -30,9 +31,7 @@ class LanguageSelectionPageState extends State { @override void initState() { super.initState(); - _baseLanguage = - MatrixState.pangeaController.languageController.systemLanguage; - + _baseLanguage = LanguageController.systemLanguage; _setFromCache(); } diff --git a/lib/pangea/login/pages/new_course_page.dart b/lib/pangea/login/pages/new_course_page.dart index fafc3e989..44585f51c 100644 --- a/lib/pangea/login/pages/new_course_page.dart +++ b/lib/pangea/login/pages/new_course_page.dart @@ -51,7 +51,7 @@ class NewCoursePageState extends State { super.initState(); _targetLanguageFilter.value = - MatrixState.pangeaController.languageController.userL2; + MatrixState.pangeaController.userController.userL2; _loadCourses(); } diff --git a/lib/pangea/login/pages/public_courses_page.dart b/lib/pangea/login/pages/public_courses_page.dart index 7710d518e..b64c83e3b 100644 --- a/lib/pangea/login/pages/public_courses_page.dart +++ b/lib/pangea/login/pages/public_courses_page.dart @@ -44,7 +44,7 @@ class PublicCoursesPageState extends State { void initState() { super.initState(); - final target = MatrixState.pangeaController.languageController.userL2; + final target = MatrixState.pangeaController.userController.userL2; if (target != null) { setTargetLanguageFilter(target); } @@ -120,7 +120,7 @@ class PublicCoursesPageState extends State { GetLocalizedCoursesRequest( coursePlanIds: discoveredCourses.map((c) => c.courseId).toSet().toList(), - l1: MatrixState.pangeaController.languageController.activeL1Code()!, + l1: MatrixState.pangeaController.userController.userL1Code!, ), ); final searchResult = resp.coursePlans; diff --git a/lib/pangea/message_token_text/token_emoji_button.dart b/lib/pangea/message_token_text/token_emoji_button.dart index f0e1210a8..b60faa0fd 100644 --- a/lib/pangea/message_token_text/token_emoji_button.dart +++ b/lib/pangea/message_token_text/token_emoji_button.dart @@ -100,8 +100,7 @@ class TokenEmojiButtonState extends State overlayKey: "overlay_emoji_selector", context: context, cardToShow: LemmaMeaningBuilder( - langCode: - MatrixState.pangeaController.languageController.activeL2Code()!, + langCode: MatrixState.pangeaController.userController.userL2Code!, constructId: widget.token!.vocabConstructID, builder: (context, controller) { return Material( diff --git a/lib/pangea/message_token_text/tokens_util.dart b/lib/pangea/message_token_text/tokens_util.dart index 35abb916c..6061eb3a5 100644 --- a/lib/pangea/message_token_text/tokens_util.dart +++ b/lib/pangea/message_token_text/tokens_util.dart @@ -81,7 +81,7 @@ class TokensUtil { } final messageInUserL2 = event.messageDisplayLangCode.split("-")[0] == - MatrixState.pangeaController.languageController.userL2?.langCodeShort; + MatrixState.pangeaController.userController.userL2?.langCodeShort; final cached = _getCachedNewTokens(event.eventId); if (cached != null) { diff --git a/lib/pangea/morphs/morph_meaning/morph_info_repo.dart b/lib/pangea/morphs/morph_meaning/morph_info_repo.dart index 049dffcda..76ff693f0 100644 --- a/lib/pangea/morphs/morph_meaning/morph_info_repo.dart +++ b/lib/pangea/morphs/morph_meaning/morph_info_repo.dart @@ -86,12 +86,10 @@ class MorphInfoRepo { }) async { final res = await _get( MorphInfoRequest( - userL1: - MatrixState.pangeaController.languageController.userL1?.langCode ?? - LanguageKeys.defaultLanguage, - userL2: - MatrixState.pangeaController.languageController.userL2?.langCode ?? - LanguageKeys.defaultLanguage, + userL1: MatrixState.pangeaController.userController.userL1?.langCode ?? + LanguageKeys.defaultLanguage, + userL2: MatrixState.pangeaController.userController.userL2?.langCode ?? + LanguageKeys.defaultLanguage, ), ); final morph = res.getFeatureByCode(feature.name); @@ -107,10 +105,10 @@ class MorphInfoRepo { required String defintion, }) async { final userL1 = - MatrixState.pangeaController.languageController.userL1?.langCode ?? + MatrixState.pangeaController.userController.userL1?.langCode ?? LanguageKeys.defaultLanguage; final userL2 = - MatrixState.pangeaController.languageController.userL2?.langCode ?? + MatrixState.pangeaController.userController.userL2?.langCode ?? LanguageKeys.defaultLanguage; final userL1Short = userL1.split('-').first; final userL2Short = userL2.split('-').first; diff --git a/lib/pangea/morphs/morph_repo.dart b/lib/pangea/morphs/morph_repo.dart index b3ee8e9f3..03867a4f0 100644 --- a/lib/pangea/morphs/morph_repo.dart +++ b/lib/pangea/morphs/morph_repo.dart @@ -78,7 +78,7 @@ class MorphsRepo { /// if not, we can make it async and update uses of this function /// to be async as well static Future get([LanguageModel? language]) async { - language ??= MatrixState.pangeaController.languageController.userL2; + language ??= MatrixState.pangeaController.userController.userL2; if (language == null) { return defaultMorphMapping; @@ -111,12 +111,12 @@ class MorphsRepo { } static MorphFeaturesAndTags get cached { - if (MatrixState.pangeaController.languageController.userL2?.langCodeShort == + if (MatrixState.pangeaController.userController.userL2?.langCodeShort == null) { return defaultMorphMapping; } final cachedJson = _morphsStorage.read( - MatrixState.pangeaController.languageController.userL2!.langCodeShort, + MatrixState.pangeaController.userController.userL2!.langCodeShort, ); if (cachedJson != null) { return MorphsRepo.fromJson(cachedJson); diff --git a/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart b/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart index df866e653..dcf3bc338 100644 --- a/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart +++ b/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart @@ -72,7 +72,7 @@ class _PhoneticTranscriptionWidgetState _transcription = null; }); - if (MatrixState.pangeaController.languageController.userL1 == null) { + if (MatrixState.pangeaController.userController.userL1 == null) { ErrorHandler.logError( e: Exception('User L1 is not set'), data: { @@ -85,7 +85,7 @@ class _PhoneticTranscriptionWidgetState } final req = PhoneticTranscriptionRequest( arc: LanguageArc( - l1: MatrixState.pangeaController.languageController.userL1!, + l1: MatrixState.pangeaController.userController.userL1!, l2: widget.textLanguage, ), content: PangeaTokenText.fromString(widget.text), diff --git a/lib/pangea/practice_activities/practice_selection_repo.dart b/lib/pangea/practice_activities/practice_selection_repo.dart index ff7c5287f..8bc9ddf72 100644 --- a/lib/pangea/practice_activities/practice_selection_repo.dart +++ b/lib/pangea/practice_activities/practice_selection_repo.dart @@ -39,7 +39,7 @@ class PracticeSelectionRepo { String messageLanguage, List tokens, ) { - final userL2 = MatrixState.pangeaController.languageController.userL2; + final userL2 = MatrixState.pangeaController.userController.userL2; if (userL2?.langCodeShort != messageLanguage.split("-").first) { return null; } @@ -61,7 +61,7 @@ class PracticeSelectionRepo { required String langCode, }) { if (langCode.split("-")[0] != - MatrixState.pangeaController.languageController.userL2?.langCodeShort) { + MatrixState.pangeaController.userController.userL2?.langCodeShort) { return PracticeSelection({}); } diff --git a/lib/pangea/space_analytics/download_space_analytics_dialog.dart b/lib/pangea/space_analytics/download_space_analytics_dialog.dart index 850354921..fa9a31afe 100644 --- a/lib/pangea/space_analytics/download_space_analytics_dialog.dart +++ b/lib/pangea/space_analytics/download_space_analytics_dialog.dart @@ -88,7 +88,7 @@ class DownloadAnalyticsDialogState extends State { } String? get userL2 => - MatrixState.pangeaController.languageController.userL2?.langCode; + MatrixState.pangeaController.userController.userL2?.langCode; Future _runDownload() async { try { diff --git a/lib/pangea/space_analytics/space_analytics.dart b/lib/pangea/space_analytics/space_analytics.dart index 105036a1a..a444796b8 100644 --- a/lib/pangea/space_analytics/space_analytics.dart +++ b/lib/pangea/space_analytics/space_analytics.dart @@ -125,7 +125,7 @@ class SpaceAnalyticsState extends State { Room? get room => Matrix.of(context).client.getRoomById(widget.roomId); LanguageModel? get _userL2 { - final l2 = MatrixState.pangeaController.languageController.userL2; + final l2 = MatrixState.pangeaController.userController.userL2; if (l2 == null) return null; // Attempt to find the language model by its short code, since analytics diff --git a/lib/pangea/spaces/widgets/join_with_link_page.dart b/lib/pangea/spaces/widgets/join_with_link_page.dart index b206e7825..89d3a3ac9 100644 --- a/lib/pangea/spaces/widgets/join_with_link_page.dart +++ b/lib/pangea/spaces/widgets/join_with_link_page.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:fluffychat/pangea/spaces/space_code_controller.dart'; +import 'package:fluffychat/pangea/spaces/space_code_repo.dart'; //if on home with classcode in url and not logged in, then save it soemhow and after llogin, join class automatically //if on home with classcode in url and logged in, then join class automatically @@ -33,7 +33,7 @@ class _JoinClassWithLinkState extends State { } if (widget.classCode != null) { - await SpaceCodeController.cacheSpaceCode(widget.classCode!); + await SpaceCodeRepo.setSpaceCode(widget.classCode!); } context.push("/home"); }); diff --git a/lib/pangea/text_to_speech/tts_controller.dart b/lib/pangea/text_to_speech/tts_controller.dart index 6ca6745f5..4c2676bad 100644 --- a/lib/pangea/text_to_speech/tts_controller.dart +++ b/lib/pangea/text_to_speech/tts_controller.dart @@ -275,12 +275,10 @@ class TtsController { text: text, langCode: langCode, tokens: tokens, - userL1: - MatrixState.pangeaController.languageController.activeL1Code() ?? - LanguageKeys.unknownLanguage, - userL2: - MatrixState.pangeaController.languageController.activeL2Code() ?? - LanguageKeys.unknownLanguage, + userL1: MatrixState.pangeaController.userController.userL1Code ?? + LanguageKeys.unknownLanguage, + userL2: MatrixState.pangeaController.userController.userL2Code ?? + LanguageKeys.unknownLanguage, ), ); loadingChoreoStream.add(false); diff --git a/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart b/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart index 33a7e7775..95c987eda 100644 --- a/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart +++ b/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart @@ -135,9 +135,9 @@ class TokenInfoFeedbackDialog extends StatelessWidget { final req = PhoneticTranscriptionRequest( arc: LanguageArc( l1: PLanguageStore.byLangCode(requestData.wordCardL1) ?? - MatrixState.pangeaController.languageController.userL1!, + MatrixState.pangeaController.userController.userL1!, l2: PLanguageStore.byLangCode(langCode) ?? - MatrixState.pangeaController.languageController.userL2!, + MatrixState.pangeaController.userController.userL2!, ), content: response.content, ); diff --git a/lib/pangea/toolbar/reading_assistance_input_row/practice_match_item.dart b/lib/pangea/toolbar/reading_assistance_input_row/practice_match_item.dart index 87d0e8adc..d05042c15 100644 --- a/lib/pangea/toolbar/reading_assistance_input_row/practice_match_item.dart +++ b/lib/pangea/toolbar/reading_assistance_input_row/practice_match_item.dart @@ -58,8 +58,7 @@ class PracticeMatchItemState extends State { setState(() => _isPlaying = true); } try { - final l2 = - MatrixState.pangeaController.languageController.activeL2Code(); + final l2 = MatrixState.pangeaController.userController.userL2Code; if (l2 != null) { await TtsController.tryToSpeak( widget.audioContent!, diff --git a/lib/pangea/toolbar/widgets/overlay_message.dart b/lib/pangea/toolbar/widgets/overlay_message.dart index 1f530817c..936ebe525 100644 --- a/lib/pangea/toolbar/widgets/overlay_message.dart +++ b/lib/pangea/toolbar/widgets/overlay_message.dart @@ -494,8 +494,8 @@ class _MessageBubbleTranscription extends StatelessWidget { onClick: onTokenSelected, isSelected: isTokenSelected, ), - if (MatrixState.pangeaController.languageController - .showTranscription) + if (MatrixState + .pangeaController.userController.showTranscription) PhoneticTranscriptionWidget( text: transcription.transcript.text, textLanguage: PLanguageStore.byLangCode( diff --git a/lib/pangea/toolbar/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/toolbar/widgets/practice_activity/practice_activity_card.dart index 4cba63fb1..f036ae220 100644 --- a/lib/pangea/toolbar/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/toolbar/widgets/practice_activity/practice_activity_card.dart @@ -65,7 +65,7 @@ class PracticeActivityCardState extends State { Future _fetchActivity() async { _activityState.value = const AsyncState.loading(); - if (!MatrixState.pangeaController.languageController.languagesSet) { + if (!MatrixState.pangeaController.userController.languagesSet) { _activityState.value = const AsyncState.error("Error fetching activity"); return; } diff --git a/lib/pangea/toolbar/widgets/practice_controller.dart b/lib/pangea/toolbar/widgets/practice_controller.dart index 7df582262..cb055f395 100644 --- a/lib/pangea/toolbar/widgets/practice_controller.dart +++ b/lib/pangea/toolbar/widgets/practice_controller.dart @@ -59,8 +59,8 @@ class PracticeController with ChangeNotifier { PracticeTarget target, ) async { final req = MessageActivityRequest( - userL1: MatrixState.pangeaController.languageController.userL1!.langCode, - userL2: MatrixState.pangeaController.languageController.userL2!.langCode, + userL1: MatrixState.pangeaController.userController.userL1!.langCode, + userL2: MatrixState.pangeaController.userController.userL2!.langCode, messageText: pangeaMessageEvent.messageDisplayText, messageTokens: pangeaMessageEvent.messageDisplayRepresentation?.tokens ?? [], diff --git a/lib/pangea/toolbar/widgets/reading_assistance_content.dart b/lib/pangea/toolbar/widgets/reading_assistance_content.dart index eaaf03435..7086f7b1e 100644 --- a/lib/pangea/toolbar/widgets/reading_assistance_content.dart +++ b/lib/pangea/toolbar/widgets/reading_assistance_content.dart @@ -60,8 +60,7 @@ class ReadingAssistanceContent extends StatelessWidget { overlayController.pangeaMessageEvent.messageDisplayLangCode, tokens: tokens ?? [], selectedToken: selectedTokenIndex, - wordCardL1: - MatrixState.pangeaController.languageController.activeL1Code()!, + wordCardL1: MatrixState.pangeaController.userController.userL1Code!, ); overlayController.widget.chatController.showTokenFeedbackDialog( requestData, diff --git a/lib/pangea/toolbar/widgets/select_mode_controller.dart b/lib/pangea/toolbar/widgets/select_mode_controller.dart index cfe98d4c5..4b330100e 100644 --- a/lib/pangea/toolbar/widgets/select_mode_controller.dart +++ b/lib/pangea/toolbar/widgets/select_mode_controller.dart @@ -19,8 +19,8 @@ class _TranscriptionLoader extends AsyncLoader { @override Future fetch() => messageEvent.requestSpeechToText( - MatrixState.pangeaController.languageController.userL1!.langCodeShort, - MatrixState.pangeaController.languageController.userL2!.langCodeShort, + MatrixState.pangeaController.userController.userL1!.langCodeShort, + MatrixState.pangeaController.userController.userL2!.langCodeShort, ); } @@ -30,12 +30,12 @@ class _STTTranslationLoader extends AsyncLoader { @override Future fetch() => messageEvent.requestSttTranslation( - langCode: MatrixState - .pangeaController.languageController.userL1!.langCodeShort, - l1Code: MatrixState - .pangeaController.languageController.userL1!.langCodeShort, - l2Code: MatrixState - .pangeaController.languageController.userL2!.langCodeShort, + langCode: + MatrixState.pangeaController.userController.userL1!.langCodeShort, + l1Code: + MatrixState.pangeaController.userController.userL1!.langCodeShort, + l2Code: + MatrixState.pangeaController.userController.userL2!.langCodeShort, ); } @@ -129,7 +129,7 @@ class SelectModeController { if (messageEvent.event.messageType == MessageTypes.Text) { final matchesL2 = messageEvent.messageDisplayLangCode.split("-").first == - MatrixState.pangeaController.languageController.userL2!.langCodeShort; + MatrixState.pangeaController.userController.userL2!.langCodeShort; return matchesL2 ? textModes : [SelectMode.translate]; } diff --git a/lib/pangea/toolbar/widgets/word_zoom/lemma_meaning_builder.dart b/lib/pangea/toolbar/widgets/word_zoom/lemma_meaning_builder.dart index a1dc4f238..7ca56172b 100644 --- a/lib/pangea/toolbar/widgets/word_zoom/lemma_meaning_builder.dart +++ b/lib/pangea/toolbar/widgets/word_zoom/lemma_meaning_builder.dart @@ -50,9 +50,8 @@ class LemmaMeaningBuilderState extends State { lemma: widget.constructId.lemma, partOfSpeech: widget.constructId.category, lemmaLang: widget.langCode, - userL1: - MatrixState.pangeaController.languageController.userL1?.langCode ?? - LanguageKeys.defaultLanguage, + userL1: MatrixState.pangeaController.userController.userL1?.langCode ?? + LanguageKeys.defaultLanguage, ); Future _fetchLemmaMeaning() async { diff --git a/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart b/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart index a85c591aa..cd0ac0d33 100644 --- a/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart +++ b/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart @@ -126,8 +126,8 @@ class WordZoomWidget extends StatelessWidget { spacing: 12.0, mainAxisSize: MainAxisSize.min, children: [ - if (MatrixState.pangeaController - .languageController.showTranscription) + if (MatrixState.pangeaController.userController + .showTranscription) PhoneticTranscriptionWidget( text: token.content, textLanguage: PLanguageStore.byLangCode( diff --git a/lib/pangea/user/controllers/permissions_controller.dart b/lib/pangea/user/controllers/permissions_controller.dart deleted file mode 100644 index d9ca47825..000000000 --- a/lib/pangea/user/controllers/permissions_controller.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'package:matrix/matrix.dart'; - -import 'package:fluffychat/pangea/common/controllers/base_controller.dart'; -import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/tool_settings_enum.dart'; - -class PermissionsController extends BaseController { - late PangeaController _pangeaController; - - PermissionsController(PangeaController pangeaController) : super() { - _pangeaController = pangeaController; - } - - /// Returns false if user is null - bool isUser18() { - final DateTime? dob = - _pangeaController.userController.profile.userSettings.dateOfBirth; - if (dob == null) return false; - final today = DateTime.now(); - final age = today.year - dob.year; - - // Check if the birthday has occurred yet this year - final hasHadBirthdayThisYear = (today.month > dob.month) || - (today.month == dob.month && today.day >= dob.day); - - // Return true if they are 18 or older - return age > 18 || (age == 18 && hasHadBirthdayThisYear); - } - - bool canShareVideo(String? roomID) => isUser18(); - - bool canSharePhoto(String? roomID) => true; - - bool canShareFile(String? roomID) => true; - - bool canShareLocation(String? roomID) => isUser18(); - - bool userToolSetting(ToolSetting setting) { - switch (setting) { - case ToolSetting.interactiveTranslator: - return _pangeaController - .userController.profile.toolSettings.interactiveTranslator; - case ToolSetting.interactiveGrammar: - return _pangeaController - .userController.profile.toolSettings.interactiveGrammar; - case ToolSetting.immersionMode: - return _pangeaController - .userController.profile.toolSettings.immersionMode; - case ToolSetting.definitions: - return _pangeaController - .userController.profile.toolSettings.definitions; - case ToolSetting.autoIGC: - return _pangeaController.userController.profile.toolSettings.autoIGC; - default: - return false; - } - } - - bool isToolEnabled(ToolSetting setting) { - // Rules can't be edited; default to true - return userToolSetting(setting); - // if (room?.isSpaceAdmin ?? false) { - // return userToolSetting(setting); - // } - // final int? classPermission = - // room != null ? classLanguageToolPermission(room, setting) : 1; - // if (classPermission == 0) return false; - // if (classPermission == 2) return true; - // return userToolSetting(setting); - } - - bool isWritingAssistanceEnabled(Room? room) { - // Rules can't be edited; default to true - return true; - // return isToolEnabled(ToolSetting.interactiveTranslator, room) && - // isToolEnabled(ToolSetting.interactiveGrammar, room); - } -} diff --git a/lib/pangea/user/controllers/user_controller.dart b/lib/pangea/user/controllers/user_controller.dart index b548be8d4..a0a81da20 100644 --- a/lib/pangea/user/controllers/user_controller.dart +++ b/lib/pangea/user/controllers/user_controller.dart @@ -13,6 +13,8 @@ import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; +import 'package:fluffychat/pangea/learning_settings/controllers/language_controller.dart'; +import 'package:fluffychat/pangea/learning_settings/enums/tool_settings_enum.dart'; import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; import 'package:fluffychat/pangea/user/models/activities_profile_model.dart'; @@ -107,8 +109,8 @@ class UserController { waitForDataInSync = false, }) async { await initialize(); - final prevTargetLang = _pangeaController.languageController.userL2; - final prevBaseLang = _pangeaController.languageController.userL1; + final prevTargetLang = _pangeaController.userController.userL2; + final prevBaseLang = _pangeaController.userController.userL1; final prevHash = profile.hashCode; final Profile updatedProfile = update(profile); @@ -119,12 +121,12 @@ class UserController { await updatedProfile.saveProfileData(waitForDataInSync: waitForDataInSync); - if ((prevTargetLang != _pangeaController.languageController.userL2) || - (prevBaseLang != _pangeaController.languageController.userL1)) { + if ((prevTargetLang != _pangeaController.userController.userL2) || + (prevBaseLang != _pangeaController.userController.userL1)) { languageStream.add( LanguageUpdate( - baseLang: _pangeaController.languageController.userL1!, - targetLang: _pangeaController.languageController.userL2!, + baseLang: _pangeaController.userController.userL1!, + targetLang: _pangeaController.userController.userL2!, prevBaseLang: prevBaseLang, prevTargetLang: prevTargetLang, ), @@ -155,7 +157,7 @@ class UserController { if (profile.userSettings.targetLanguage != null && profile.userSettings.targetLanguage!.isNotEmpty && - _pangeaController.languageController.userL2 == null) { + _pangeaController.userController.userL2 == null) { // update the language list and send an update to refresh analytics summary await PLanguageStore.initialize(forceRefresh: true); } @@ -364,8 +366,8 @@ class UserController { LanguageModel? baseLanguage, LanguageModel? targetLanguage, }) async { - targetLanguage ??= _pangeaController.languageController.userL2; - baseLanguage ??= _pangeaController.languageController.userL1; + targetLanguage ??= _pangeaController.userController.userL2; + baseLanguage ??= _pangeaController.userController.userL1; if (targetLanguage == null || analyticsProfile == null) return; final analyticsRoom = @@ -426,7 +428,7 @@ class UserController { } Future addXPOffset(int offset) async { - final targetLanguage = _pangeaController.languageController.userL2; + final targetLanguage = _pangeaController.userController.userL2; if (targetLanguage == null || analyticsProfile == null) return; analyticsProfile!.addXPOffset( @@ -525,4 +527,66 @@ class UserController { return AnalyticsProfileModel(); } } + + bool isToolEnabled(ToolSetting setting) { + return userToolSetting(setting); + } + + bool userToolSetting(ToolSetting setting) { + switch (setting) { + case ToolSetting.interactiveTranslator: + return profile.toolSettings.interactiveTranslator; + case ToolSetting.interactiveGrammar: + return profile.toolSettings.interactiveGrammar; + case ToolSetting.immersionMode: + return profile.toolSettings.immersionMode; + case ToolSetting.definitions: + return profile.toolSettings.definitions; + case ToolSetting.autoIGC: + return profile.toolSettings.autoIGC; + default: + return false; + } + } + + String? get userL1Code { + final source = profile.userSettings.sourceLanguage; + return source == null || source.isEmpty + ? LanguageController.systemLanguage?.langCode + : source; + } + + String? get userL2Code { + final target = profile.userSettings.targetLanguage; + return target == null || target.isEmpty ? null : target; + } + + LanguageModel? get userL1 { + if (userL1Code == null) return null; + final langModel = PLanguageStore.byLangCode(userL1Code!); + return langModel?.langCode == LanguageKeys.unknownLanguage + ? null + : langModel; + } + + LanguageModel? get userL2 { + if (userL2Code == null) return null; + final langModel = PLanguageStore.byLangCode(userL2Code!); + return langModel?.langCode == LanguageKeys.unknownLanguage + ? null + : langModel; + } + + bool get languagesSet => + userL1Code != null && + userL2Code != null && + userL1Code!.isNotEmpty && + userL2Code!.isNotEmpty && + userL1Code != LanguageKeys.unknownLanguage && + userL2Code != LanguageKeys.unknownLanguage; + + bool get showTranscription => + (userL1 != null && userL2 != null && userL1?.script != userL2?.script) || + (userL1?.script != LanguageKeys.unknownLanguage || + userL2?.script == LanguageKeys.unknownLanguage); } diff --git a/lib/pangea/word_bank/vocab_bank_repo.dart b/lib/pangea/word_bank/vocab_bank_repo.dart index 84b8c852d..a1e9404f0 100644 --- a/lib/pangea/word_bank/vocab_bank_repo.dart +++ b/lib/pangea/word_bank/vocab_bank_repo.dart @@ -162,9 +162,9 @@ class VocabRepo { } static VocabResponse placeholderData([LanguageModel? language]) { - language ??= MatrixState.pangeaController.languageController.userL2 == null + language ??= MatrixState.pangeaController.userController.userL2 == null ? PLanguageStore.byLangCode(LanguageKeys.defaultLanguage) - : MatrixState.pangeaController.languageController.userL2!; + : MatrixState.pangeaController.userController.userL2!; //TODO - move this to the server and fill out all our languages final Map placeholder = { From 3ac833981737204ac99095e475b7c43d639a9f3e Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 4 Dec 2025 10:06:59 -0500 Subject: [PATCH 7/9] split language related and learning settings related folders --- lib/config/routes.dart | 2 +- lib/main.dart | 4 +- lib/pages/chat/chat.dart | 10 +- .../activity_plan_request.dart | 4 +- .../activity_chat_extension.dart | 2 +- .../activity_summary_widget.dart | 1 - .../client_analytics_extension.dart | 2 +- .../get_analytics_controller.dart | 2 +- .../level_up/level_up_manager.dart | 2 +- .../level_up/level_up_popup.dart | 2 +- .../put_analytics_controller.dart | 2 +- .../learning_progress_indicators.dart | 2 +- .../chat/widgets/pangea_chat_input_row.dart | 2 +- .../models/bot_options_model.dart | 6 +- .../widgets/language_level_dropdown.dart | 2 +- lib/pangea/choreographer/choreographer.dart | 4 +- .../choreographer/igc/start_igc_button.dart | 2 +- lib/pangea/choreographer/it/it_bar.dart | 2 +- .../choreographer/it/word_data_card.dart | 2 +- .../common/controllers/pangea_controller.dart | 4 +- lib/pangea/common/network/requests.dart | 1 - lib/pangea/common/utils/overlay.dart | 2 +- .../constructs/construct_identifier.dart | 2 +- .../course_info_chip_widget.dart | 1 - .../course_language_filter.dart | 2 +- .../course_plans/courses/course_filter.dart | 4 +- .../courses/course_plan_model.dart | 8 +- .../event_wrappers/pangea_message_event.dart | 7 +- .../pangea_representation_event.dart | 2 +- lib/pangea/events/repo/token_api_models.dart | 2 +- .../reset_instructions_list_tile.dart | 2 +- .../enums => languages}/l2_support_enum.dart | 4 +- lib/pangea/languages/language_arc_model.dart | 25 +++ .../language_constants.dart | 29 ++-- .../models => languages}/language_model.dart | 33 +--- lib/pangea/languages/language_repo.dart | 53 +++++++ .../language_service.dart} | 8 +- .../utils => languages}/locale_provider.dart | 0 lib/pangea/languages/p_language_store.dart | 145 ++++++++++++++++++ .../{utils => }/country_display.dart | 0 .../{widgets => }/country_picker_tile.dart | 4 +- .../{enums => }/language_level_type_enum.dart | 10 +- .../language_mismatch_popup.dart | 0 .../{repo => }/language_mismatch_repo.dart | 0 .../{widgets => }/p_language_dialog.dart | 14 +- .../{widgets => }/p_language_dropdown.dart | 11 +- .../{widgets => }/p_question_container.dart | 0 .../p_settings_switch_list_tile.dart | 0 .../learning_settings/repo/language_repo.dart | 40 ----- .../{pages => }/settings_learning.dart | 87 ++++++++++- .../{pages => }/settings_learning_view.dart | 93 +---------- .../{enums => }/tool_settings_enum.dart | 0 .../utils/p_language_store.dart | 133 ---------------- .../learning_settings/utils/shared_prefs.dart | 37 ----- .../learning_settings/widgets/flag.dart | 22 --- .../pages/create_pangea_account_page.dart | 8 +- .../login/pages/language_selection_page.dart | 10 +- lib/pangea/login/pages/new_course_page.dart | 4 +- .../login/pages/public_courses_page.dart | 2 +- .../morphs/morph_meaning/morph_info_repo.dart | 2 +- lib/pangea/morphs/morph_repo.dart | 2 +- .../phonetic_transcription_request.dart | 2 +- .../phonetic_transcription_response.dart | 2 +- .../phonetic_transcription_widget.dart | 3 +- .../analytics_requests_repo.dart | 2 +- .../space_analytics/space_analytics.dart | 4 +- .../space_analytics/space_analytics_view.dart | 2 +- lib/pangea/text_to_speech/tts_controller.dart | 2 +- .../token_info_feedback_dialog.dart | 4 +- .../toolbar/widgets/overlay_message.dart | 4 +- .../word_zoom/lemma_meaning_builder.dart | 2 +- .../widgets/word_zoom/word_zoom_widget.dart | 4 +- .../user/controllers/user_controller.dart | 12 +- .../user/models/analytics_profile_model.dart | 4 +- lib/pangea/user/models/user_model.dart | 8 +- lib/pangea/word_bank/vocab_bank_repo.dart | 6 +- lib/pangea/word_bank/vocab_request.dart | 2 +- lib/utils/background_push.dart | 2 +- lib/widgets/fluffy_chat_app.dart | 2 +- lib/widgets/matrix.dart | 2 +- 80 files changed, 451 insertions(+), 486 deletions(-) rename lib/pangea/{learning_settings/enums => languages}/l2_support_enum.dart (97%) create mode 100644 lib/pangea/languages/language_arc_model.dart rename lib/pangea/{learning_settings/constants => languages}/language_constants.dart (98%) rename lib/pangea/{learning_settings/models => languages}/language_model.dart (94%) create mode 100644 lib/pangea/languages/language_repo.dart rename lib/pangea/{learning_settings/controllers/language_controller.dart => languages/language_service.dart} (72%) rename lib/pangea/{learning_settings/utils => languages}/locale_provider.dart (100%) create mode 100644 lib/pangea/languages/p_language_store.dart rename lib/pangea/learning_settings/{utils => }/country_display.dart (100%) rename lib/pangea/learning_settings/{widgets => }/country_picker_tile.dart (96%) rename lib/pangea/learning_settings/{enums => }/language_level_type_enum.dart (96%) rename lib/pangea/learning_settings/{widgets => }/language_mismatch_popup.dart (100%) rename lib/pangea/learning_settings/{repo => }/language_mismatch_repo.dart (100%) rename lib/pangea/learning_settings/{widgets => }/p_language_dialog.dart (91%) rename lib/pangea/learning_settings/{widgets => }/p_language_dropdown.dart (96%) rename lib/pangea/learning_settings/{widgets => }/p_question_container.dart (100%) rename lib/pangea/learning_settings/{widgets => }/p_settings_switch_list_tile.dart (100%) delete mode 100644 lib/pangea/learning_settings/repo/language_repo.dart rename lib/pangea/learning_settings/{pages => }/settings_learning.dart (74%) rename lib/pangea/learning_settings/{pages => }/settings_learning_view.dart (81%) rename lib/pangea/learning_settings/{enums => }/tool_settings_enum.dart (100%) delete mode 100644 lib/pangea/learning_settings/utils/p_language_store.dart delete mode 100644 lib/pangea/learning_settings/utils/shared_prefs.dart delete mode 100644 lib/pangea/learning_settings/widgets/flag.dart diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 4b1fe560a..eecc155c1 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -43,7 +43,7 @@ import 'package:fluffychat/pangea/course_creation/selected_course_page.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/find_your_people/find_your_people_constants.dart'; import 'package:fluffychat/pangea/guard/p_vguard.dart'; -import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart'; +import 'package:fluffychat/pangea/learning_settings/settings_learning.dart'; import 'package:fluffychat/pangea/login/pages/add_course_page.dart'; import 'package:fluffychat/pangea/login/pages/course_code_page.dart'; import 'package:fluffychat/pangea/login/pages/create_pangea_account_page.dart'; diff --git a/lib/main.dart b/lib/main.dart index 6326a8b06..ac55d22a4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,8 +13,8 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/common/utils/firebase_analytics.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/locale_provider.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; +import 'package:fluffychat/pangea/languages/locale_provider.dart'; +import 'package:fluffychat/pangea/languages/p_language_store.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'config/setting_keys.dart'; diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index bde9816cd..c34851c3b 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -58,10 +58,10 @@ import 'package:fluffychat/pangea/events/models/representation_content_model.dar import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/instructions/instructions_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; -import 'package:fluffychat/pangea/learning_settings/controllers/language_controller.dart'; -import 'package:fluffychat/pangea/learning_settings/repo/language_mismatch_repo.dart'; -import 'package:fluffychat/pangea/learning_settings/widgets/p_language_dialog.dart'; +import 'package:fluffychat/pangea/languages/language_constants.dart'; +import 'package:fluffychat/pangea/languages/language_service.dart'; +import 'package:fluffychat/pangea/learning_settings/language_mismatch_repo.dart'; +import 'package:fluffychat/pangea/learning_settings/p_language_dialog.dart'; import 'package:fluffychat/pangea/message_token_text/tokens_util.dart'; import 'package:fluffychat/pangea/spaces/widgets/load_participants_builder.dart'; import 'package:fluffychat/pangea/subscription/widgets/paywall_card.dart'; @@ -533,7 +533,7 @@ class ChatController extends State Future.delayed(const Duration(seconds: 1), () async { if (!mounted) return; - LanguageController.showDialogOnEmptyLanguage( + LanguageService.showDialogOnEmptyLanguage( context, () => () => setState(() {}), ); diff --git a/lib/pangea/activity_planner/activity_plan_request.dart b/lib/pangea/activity_planner/activity_plan_request.dart index d75772745..37c62beef 100644 --- a/lib/pangea/activity_planner/activity_plan_request.dart +++ b/lib/pangea/activity_planner/activity_plan_request.dart @@ -1,6 +1,6 @@ import 'package:fluffychat/pangea/activity_generator/media_enum.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; +import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dart'; class ActivityPlanRequest { final String topic; @@ -49,7 +49,7 @@ class ActivityPlanRequest { objective: json[ModelKey.activityRequestObjective], media: MediaEnum.nan.fromString(json[ModelKey.activityRequestMedia]), cefrLevel: json[ModelKey.activityRequestCefrLevel] != null - ? LanguageLevelTypeEnumExtension.fromString( + ? LanguageLevelTypeEnum.fromString( json[ModelKey.activityRequestCefrLevel], ) : LanguageLevelTypeEnum.a1, diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart index 1cf309eca..b1f6ccb8c 100644 --- a/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart +++ b/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart @@ -4,7 +4,7 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; import 'package:fluffychat/pangea/instructions/instructions_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/repo/language_mismatch_repo.dart'; +import 'package:fluffychat/pangea/learning_settings/language_mismatch_repo.dart'; import 'package:fluffychat/widgets/matrix.dart'; extension ActivityMenuLogic on ChatController { diff --git a/lib/pangea/activity_sessions/activity_summary_widget.dart b/lib/pangea/activity_sessions/activity_summary_widget.dart index 2be683199..82a9dfaa9 100644 --- a/lib/pangea/activity_sessions/activity_summary_widget.dart +++ b/lib/pangea/activity_sessions/activity_summary_widget.dart @@ -17,7 +17,6 @@ import 'package:fluffychat/pangea/activity_sessions/activity_role_model.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_session_details_row.dart'; import 'package:fluffychat/pangea/common/widgets/url_image_widget.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; class ActivitySummary extends StatelessWidget { final ActivityPlanModel activity; diff --git a/lib/pangea/analytics_misc/client_analytics_extension.dart b/lib/pangea/analytics_misc/client_analytics_extension.dart index 0a8f9d431..e4d36c11d 100644 --- a/lib/pangea/analytics_misc/client_analytics_extension.dart +++ b/lib/pangea/analytics_misc/client_analytics_extension.dart @@ -11,7 +11,7 @@ import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart import 'package:fluffychat/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; import 'package:fluffychat/widgets/matrix.dart'; extension AnalyticsClientExtension on Client { diff --git a/lib/pangea/analytics_misc/get_analytics_controller.dart b/lib/pangea/analytics_misc/get_analytics_controller.dart index d12b0cc18..b79c6cd4a 100644 --- a/lib/pangea/analytics_misc/get_analytics_controller.dart +++ b/lib/pangea/analytics_misc/get_analytics_controller.dart @@ -21,7 +21,7 @@ import 'package:fluffychat/pangea/constructs/construct_repo.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; import 'package:fluffychat/pangea/practice_activities/practice_selection_repo.dart'; import 'package:fluffychat/widgets/matrix.dart'; diff --git a/lib/pangea/analytics_misc/level_up/level_up_manager.dart b/lib/pangea/analytics_misc/level_up/level_up_manager.dart index 3e8a7e5f3..f446f24a3 100644 --- a/lib/pangea/analytics_misc/level_up/level_up_manager.dart +++ b/lib/pangea/analytics_misc/level_up/level_up_manager.dart @@ -6,7 +6,7 @@ import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; import 'package:fluffychat/pangea/constructs/construct_repo.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; import 'package:fluffychat/widgets/matrix.dart'; class LevelUpManager { diff --git a/lib/pangea/analytics_misc/level_up/level_up_popup.dart b/lib/pangea/analytics_misc/level_up/level_up_popup.dart index b70506f81..53a68d397 100644 --- a/lib/pangea/analytics_misc/level_up/level_up_popup.dart +++ b/lib/pangea/analytics_misc/level_up/level_up_popup.dart @@ -20,7 +20,7 @@ import 'package:fluffychat/pangea/analytics_summary/progress_bar/progress_bar_de import 'package:fluffychat/pangea/common/widgets/error_indicator.dart'; import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart'; import 'package:fluffychat/pangea/constructs/construct_repo.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; +import 'package:fluffychat/pangea/languages/language_constants.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/mxc_image.dart'; diff --git a/lib/pangea/analytics_misc/put_analytics_controller.dart b/lib/pangea/analytics_misc/put_analytics_controller.dart index 9548f1774..5b354793d 100644 --- a/lib/pangea/analytics_misc/put_analytics_controller.dart +++ b/lib/pangea/analytics_misc/put_analytics_controller.dart @@ -12,7 +12,7 @@ import 'package:fluffychat/pangea/common/controllers/base_controller.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; import 'package:fluffychat/widgets/matrix.dart'; enum AnalyticsUpdateType { server, local, activities, init } diff --git a/lib/pangea/analytics_summary/learning_progress_indicators.dart b/lib/pangea/analytics_summary/learning_progress_indicators.dart index b46e4d000..3545792fb 100644 --- a/lib/pangea/analytics_summary/learning_progress_indicators.dart +++ b/lib/pangea/analytics_summary/learning_progress_indicators.dart @@ -12,7 +12,7 @@ import 'package:fluffychat/pangea/analytics_summary/learning_progress_bar.dart'; import 'package:fluffychat/pangea/analytics_summary/learning_progress_indicator_button.dart'; import 'package:fluffychat/pangea/analytics_summary/progress_indicator.dart'; import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart'; +import 'package:fluffychat/pangea/learning_settings/settings_learning.dart'; import 'package:fluffychat/widgets/hover_builder.dart'; import 'package:fluffychat/widgets/matrix.dart'; diff --git a/lib/pangea/chat/widgets/pangea_chat_input_row.dart b/lib/pangea/chat/widgets/pangea_chat_input_row.dart index eed64f97a..b7a28b40f 100644 --- a/lib/pangea/chat/widgets/pangea_chat_input_row.dart +++ b/lib/pangea/chat/widgets/pangea_chat_input_row.dart @@ -11,7 +11,7 @@ import 'package:fluffychat/pangea/choreographer/choreo_constants.dart'; import 'package:fluffychat/pangea/choreographer/choreographer_send_button.dart'; import 'package:fluffychat/pangea/choreographer/choreographer_state_extension.dart'; import 'package:fluffychat/pangea/choreographer/igc/start_igc_button.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/matrix.dart'; diff --git a/lib/pangea/chat_settings/models/bot_options_model.dart b/lib/pangea/chat_settings/models/bot_options_model.dart index 0bc2779f9..4108890d4 100644 --- a/lib/pangea/chat_settings/models/bot_options_model.dart +++ b/lib/pangea/chat_settings/models/bot_options_model.dart @@ -5,7 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:fluffychat/pangea/chat_settings/constants/bot_mode.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; +import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dart'; class BotOptionsModel { LanguageLevelTypeEnum languageLevel; @@ -63,9 +63,9 @@ class BotOptionsModel { // General Bot Options ////////////////////////////////////////////////////////////////////////// languageLevel: json[ModelKey.languageLevel] is int - ? LanguageLevelTypeEnumExtension.fromInt(json[ModelKey.languageLevel]) + ? LanguageLevelTypeEnum.fromInt(json[ModelKey.languageLevel]) : json[ModelKey.languageLevel] is String - ? LanguageLevelTypeEnumExtension.fromString( + ? LanguageLevelTypeEnum.fromString( json[ModelKey.languageLevel], ) : LanguageLevelTypeEnum.a1, diff --git a/lib/pangea/chat_settings/widgets/language_level_dropdown.dart b/lib/pangea/chat_settings/widgets/language_level_dropdown.dart index a7687790c..4fdc4bd37 100644 --- a/lib/pangea/chat_settings/widgets/language_level_dropdown.dart +++ b/lib/pangea/chat_settings/widgets/language_level_dropdown.dart @@ -6,7 +6,7 @@ import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/widgets/dropdown_text_button.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; +import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dart'; class LanguageLevelDropdown extends StatelessWidget { final LanguageLevelTypeEnum? initialLevel; diff --git a/lib/pangea/choreographer/choreographer.dart b/lib/pangea/choreographer/choreographer.dart index f7fd0e9c3..aa9b19782 100644 --- a/lib/pangea/choreographer/choreographer.dart +++ b/lib/pangea/choreographer/choreographer.dart @@ -18,8 +18,8 @@ import 'package:fluffychat/pangea/events/models/representation_content_model.dar import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/events/repo/token_api_models.dart'; import 'package:fluffychat/pangea/events/repo/tokens_repo.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/tool_settings_enum.dart'; +import 'package:fluffychat/pangea/languages/language_constants.dart'; +import 'package:fluffychat/pangea/learning_settings/tool_settings_enum.dart'; import 'package:fluffychat/pangea/subscription/controllers/subscription_controller.dart'; import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; diff --git a/lib/pangea/choreographer/igc/start_igc_button.dart b/lib/pangea/choreographer/igc/start_igc_button.dart index ffcb3ea6b..34db5f2d3 100644 --- a/lib/pangea/choreographer/igc/start_igc_button.dart +++ b/lib/pangea/choreographer/igc/start_igc_button.dart @@ -4,7 +4,7 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/choreographer/assistance_state_enum.dart'; import 'package:fluffychat/pangea/choreographer/choreographer.dart'; import 'package:fluffychat/pangea/choreographer/choreographer_state_extension.dart'; -import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart'; +import 'package:fluffychat/pangea/learning_settings/settings_learning.dart'; class StartIGCButton extends StatefulWidget { final VoidCallback onPressed; diff --git a/lib/pangea/choreographer/it/it_bar.dart b/lib/pangea/choreographer/it/it_bar.dart index 261b97872..d82a8b37b 100644 --- a/lib/pangea/choreographer/it/it_bar.dart +++ b/lib/pangea/choreographer/it/it_bar.dart @@ -12,7 +12,7 @@ import 'package:fluffychat/pangea/choreographer/it/it_feedback_card.dart'; import 'package:fluffychat/pangea/choreographer/it/word_data_card.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/common/widgets/error_indicator.dart'; -import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart'; +import 'package:fluffychat/pangea/learning_settings/settings_learning.dart'; import 'package:fluffychat/pangea/translation/full_text_translation_request_model.dart'; import 'package:fluffychat/widgets/matrix.dart'; import '../../common/utils/overlay.dart'; diff --git a/lib/pangea/choreographer/it/word_data_card.dart b/lib/pangea/choreographer/it/word_data_card.dart index b99ea0871..7f3802e08 100644 --- a/lib/pangea/choreographer/it/word_data_card.dart +++ b/lib/pangea/choreographer/it/word_data_card.dart @@ -6,7 +6,7 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/bot/utils/bot_style.dart'; import 'package:fluffychat/pangea/choreographer/it/contextual_definition_repo.dart'; import 'package:fluffychat/pangea/choreographer/it/contextual_definition_request_model.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; +import 'package:fluffychat/pangea/languages/language_constants.dart'; import 'package:fluffychat/pangea/toolbar/widgets/toolbar_content_loading_indicator.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/matrix.dart'; diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index 2c3de2d4c..f92013bfb 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -16,8 +16,8 @@ import 'package:fluffychat/pangea/chat_settings/utils/bot_client_extension.dart' import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/guard/p_vguard.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/locale_provider.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; +import 'package:fluffychat/pangea/languages/locale_provider.dart'; +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/controllers/user_controller.dart'; diff --git a/lib/pangea/common/network/requests.dart b/lib/pangea/common/network/requests.dart index 69eeb5196..f8f478db1 100644 --- a/lib/pangea/common/network/requests.dart +++ b/lib/pangea/common/network/requests.dart @@ -6,7 +6,6 @@ import 'package:http/http.dart' as http; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; import 'package:fluffychat/widgets/matrix.dart'; class Requests { diff --git a/lib/pangea/common/utils/overlay.dart b/lib/pangea/common/utils/overlay.dart index aacabc9d8..32620a162 100644 --- a/lib/pangea/common/utils/overlay.dart +++ b/lib/pangea/common/utils/overlay.dart @@ -13,7 +13,7 @@ import 'package:fluffychat/pangea/common/utils/any_state_holder.dart'; import 'package:fluffychat/pangea/common/widgets/anchored_overlay_widget.dart'; import 'package:fluffychat/pangea/common/widgets/overlay_container.dart'; import 'package:fluffychat/pangea/common/widgets/transparent_backdrop.dart'; -import 'package:fluffychat/pangea/learning_settings/widgets/language_mismatch_popup.dart'; +import 'package:fluffychat/pangea/learning_settings/language_mismatch_popup.dart'; import '../../../config/themes.dart'; import '../../../widgets/matrix.dart'; import 'error_handler.dart'; diff --git a/lib/pangea/constructs/construct_identifier.dart b/lib/pangea/constructs/construct_identifier.dart index 8ceb09234..75878cb74 100644 --- a/lib/pangea/constructs/construct_identifier.dart +++ b/lib/pangea/constructs/construct_identifier.dart @@ -10,7 +10,7 @@ import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; +import 'package:fluffychat/pangea/languages/language_constants.dart'; import 'package:fluffychat/pangea/lemmas/lemma_info_repo.dart'; import 'package:fluffychat/pangea/lemmas/lemma_info_request.dart'; import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart'; diff --git a/lib/pangea/course_creation/course_info_chip_widget.dart b/lib/pangea/course_creation/course_info_chip_widget.dart index 0f75b75af..51daf3d2b 100644 --- a/lib/pangea/course_creation/course_info_chip_widget.dart +++ b/lib/pangea/course_creation/course_info_chip_widget.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/course_plans/courses/course_plan_builder.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; class CourseInfoChip extends StatelessWidget { final IconData icon; diff --git a/lib/pangea/course_creation/course_language_filter.dart b/lib/pangea/course_creation/course_language_filter.dart index 3737ec83a..e5c45cdf5 100644 --- a/lib/pangea/course_creation/course_language_filter.dart +++ b/lib/pangea/course_creation/course_language_filter.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/course_creation/course_plan_filter_widget.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; import 'package:fluffychat/widgets/matrix.dart'; class CourseLanguageFilter extends StatelessWidget { diff --git a/lib/pangea/course_plans/courses/course_filter.dart b/lib/pangea/course_plans/courses/course_filter.dart index a43fc59d7..d34a3ce85 100644 --- a/lib/pangea/course_plans/courses/course_filter.dart +++ b/lib/pangea/course_plans/courses/course_filter.dart @@ -1,5 +1,5 @@ -import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dart'; class CourseFilter { final LanguageModel? targetLanguage; diff --git a/lib/pangea/course_plans/courses/course_plan_model.dart b/lib/pangea/course_plans/courses/course_plan_model.dart index 57e47387a..cb1606ba3 100644 --- a/lib/pangea/course_plans/courses/course_plan_model.dart +++ b/lib/pangea/course_plans/courses/course_plan_model.dart @@ -7,9 +7,9 @@ import 'package:fluffychat/pangea/course_plans/course_media/course_media_respons import 'package:fluffychat/pangea/course_plans/course_topics/course_topic_model.dart'; import 'package:fluffychat/pangea/course_plans/course_topics/course_topic_repo.dart'; import 'package:fluffychat/pangea/course_plans/course_topics/course_topic_translation_request.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.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/widgets/matrix.dart'; /// Represents a course plan in the course planner response. @@ -57,7 +57,7 @@ class CoursePlanModel { return CoursePlanModel( targetLanguage: json['target_language'] as String, languageOfInstructions: json['language_of_instructions'] as String, - cefrLevel: LanguageLevelTypeEnumExtension.fromString(json['cefr_level']), + cefrLevel: LanguageLevelTypeEnum.fromString(json['cefr_level']), title: json['title'] as String, description: json['description'] as String, uuid: json['uuid'] as String, diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart index 23e49440e..aea9115c4 100644 --- a/lib/pangea/events/event_wrappers/pangea_message_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart @@ -18,8 +18,7 @@ import 'package:fluffychat/pangea/events/repo/language_detection_repo.dart'; import 'package:fluffychat/pangea/events/repo/language_detection_request.dart'; import 'package:fluffychat/pangea/events/repo/language_detection_response.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/tool_settings_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; +import 'package:fluffychat/pangea/learning_settings/tool_settings_enum.dart'; import 'package:fluffychat/pangea/speech_to_text/audio_encoding_enum.dart'; import 'package:fluffychat/pangea/speech_to_text/speech_to_text_repo.dart'; import 'package:fluffychat/pangea/speech_to_text/speech_to_text_request_model.dart'; @@ -33,7 +32,7 @@ import 'package:fluffychat/pangea/translation/full_text_translation_request_mode import 'package:fluffychat/widgets/future_loading_dialog.dart'; import '../../../widgets/matrix.dart'; import '../../common/utils/error_handler.dart'; -import '../../learning_settings/constants/language_constants.dart'; +import '../../languages/language_constants.dart'; import '../constants/pangea_event_types.dart'; class PangeaMessageEvent { @@ -272,7 +271,7 @@ class PangeaMessageEvent { String get messageDisplayText => messageDisplayRepresentation?.text ?? body; TextDirection get textDirection => - PLanguageStore.rtlLanguageCodes.contains(messageDisplayLangCode) + LanguageConstants.rtlLanguageCodes.contains(messageDisplayLangCode) ? TextDirection.rtl : TextDirection.ltr; diff --git a/lib/pangea/events/event_wrappers/pangea_representation_event.dart b/lib/pangea/events/event_wrappers/pangea_representation_event.dart index d87ab9444..119016878 100644 --- a/lib/pangea/events/event_wrappers/pangea_representation_event.dart +++ b/lib/pangea/events/event_wrappers/pangea_representation_event.dart @@ -23,7 +23,7 @@ import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart' import 'package:fluffychat/pangea/events/repo/token_api_models.dart'; import 'package:fluffychat/pangea/events/repo/tokens_repo.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; +import 'package:fluffychat/pangea/languages/language_constants.dart'; import 'package:fluffychat/pangea/translation/full_text_translation_repo.dart'; import 'package:fluffychat/pangea/translation/full_text_translation_request_model.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; diff --git a/lib/pangea/events/repo/token_api_models.dart b/lib/pangea/events/repo/token_api_models.dart index 75eb70d4b..13c245723 100644 --- a/lib/pangea/events/repo/token_api_models.dart +++ b/lib/pangea/events/repo/token_api_models.dart @@ -1,7 +1,7 @@ import 'package:fluffychat/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/events/models/language_detection_model.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; +import 'package:fluffychat/pangea/languages/language_constants.dart'; class TokensRequestModel { /// the text to be tokenized diff --git a/lib/pangea/instructions/reset_instructions_list_tile.dart b/lib/pangea/instructions/reset_instructions_list_tile.dart index 5d3d1bafe..4154498ee 100644 --- a/lib/pangea/instructions/reset_instructions_list_tile.dart +++ b/lib/pangea/instructions/reset_instructions_list_tile.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart'; +import 'package:fluffychat/pangea/learning_settings/settings_learning.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; class ResetInstructionsListTile extends StatelessWidget { diff --git a/lib/pangea/learning_settings/enums/l2_support_enum.dart b/lib/pangea/languages/l2_support_enum.dart similarity index 97% rename from lib/pangea/learning_settings/enums/l2_support_enum.dart rename to lib/pangea/languages/l2_support_enum.dart index 5d49df919..e5083ce39 100644 --- a/lib/pangea/learning_settings/enums/l2_support_enum.dart +++ b/lib/pangea/languages/l2_support_enum.dart @@ -6,10 +6,8 @@ enum L2SupportEnum { na, alpha, beta, - full, -} + full; -extension L2SupportEnumExtension on L2SupportEnum { String get storageString { switch (this) { case L2SupportEnum.na: diff --git a/lib/pangea/languages/language_arc_model.dart b/lib/pangea/languages/language_arc_model.dart new file mode 100644 index 000000000..3e450c9c2 --- /dev/null +++ b/lib/pangea/languages/language_arc_model.dart @@ -0,0 +1,25 @@ +import 'package:fluffychat/pangea/languages/language_model.dart'; + +class LanguageArc { + final LanguageModel l1; + final LanguageModel l2; + + LanguageArc({ + required this.l1, + required this.l2, + }); + + factory LanguageArc.fromJson(Map json) { + return LanguageArc( + l1: LanguageModel.fromJson(json['l1'] as Map), + l2: LanguageModel.fromJson(json['l2'] as Map), + ); + } + + Map toJson() { + return { + 'l1': l1.toJson(), + 'l2': l2.toJson(), + }; + } +} diff --git a/lib/pangea/learning_settings/constants/language_constants.dart b/lib/pangea/languages/language_constants.dart similarity index 98% rename from lib/pangea/learning_settings/constants/language_constants.dart rename to lib/pangea/languages/language_constants.dart index 6332bfdaf..89a005ce5 100644 --- a/lib/pangea/learning_settings/constants/language_constants.dart +++ b/lib/pangea/languages/language_constants.dart @@ -1,23 +1,24 @@ -import 'package:fluffychat/pangea/events/models/language_detection_model.dart'; - class LanguageKeys { static const unknownLanguage = "unk"; - static const mixedLanguage = "mixed"; static const defaultLanguage = "en"; - static const multiLanguage = "multi"; } -class PrefKey { - static const lastFetched = 'p_lang_lastfetched'; - static const languagesKey = 'p_lang_flag'; -} +class LanguageConstants { + static final List rtlLanguageCodes = [ + 'ar', + 'arc', + 'dv', + 'fa', + 'ha', + 'he', + 'khw', + 'ks', + 'ku', + 'ps', + 'ur', + 'yi', + ]; -const LanguageDetectionModel unknownLanguageDetection = LanguageDetectionModel( - langCode: LanguageKeys.unknownLanguage, - confidence: 0.5, -); - -class FallbackLanguage { static List> get languageList => [ { "language_code": "ab", diff --git a/lib/pangea/learning_settings/models/language_model.dart b/lib/pangea/languages/language_model.dart similarity index 94% rename from lib/pangea/learning_settings/models/language_model.dart rename to lib/pangea/languages/language_model.dart index 536a266e2..73182450b 100644 --- a/lib/pangea/learning_settings/models/language_model.dart +++ b/lib/pangea/languages/language_model.dart @@ -3,9 +3,8 @@ import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/l2_support_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; +import 'package:fluffychat/pangea/languages/l2_support_enum.dart'; +import 'package:fluffychat/pangea/languages/language_constants.dart'; class LanguageModel { final String langCode; @@ -44,7 +43,7 @@ class LanguageModel { ); } - toJson() => { + Map toJson() => { 'language_code': langCode, 'language_name': displayName, 'script': script, @@ -303,7 +302,7 @@ class LanguageModel { String get langCodeShort => langCode.split('-').first; TextDirection get _defaultTextDirection { - return PLanguageStore.rtlLanguageCodes.contains(langCodeShort) + return LanguageConstants.rtlLanguageCodes.contains(langCodeShort) ? TextDirection.rtl : TextDirection.ltr; } @@ -338,27 +337,3 @@ class LanguageModel { @override int get hashCode => langCode.hashCode; } - -class LanguageArc { - final LanguageModel l1; - final LanguageModel l2; - - LanguageArc({ - required this.l1, - required this.l2, - }); - - factory LanguageArc.fromJson(Map json) { - return LanguageArc( - l1: LanguageModel.fromJson(json['l1'] as Map), - l2: LanguageModel.fromJson(json['l2'] as Map), - ); - } - - Map toJson() { - return { - 'l1': l1.toJson(), - 'l2': l2.toJson(), - }; - } -} diff --git a/lib/pangea/languages/language_repo.dart b/lib/pangea/languages/language_repo.dart new file mode 100644 index 000000000..9ad7b6329 --- /dev/null +++ b/lib/pangea/languages/language_repo.dart @@ -0,0 +1,53 @@ +import 'dart:convert'; +import 'dart:developer'; + +import 'package:flutter/foundation.dart'; + +import 'package:async/async.dart'; +import 'package:http/http.dart'; + +import 'package:fluffychat/pangea/common/config/environment.dart'; +import 'package:fluffychat/pangea/common/network/urls.dart'; +import 'package:fluffychat/pangea/common/utils/error_handler.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import '../common/network/requests.dart'; + +class LanguageRepo { + static Future>> get() async { + try { + final languages = await _fetch(); + return Result.value(languages); + } catch (e) { + return Result.error(e); + } + } + + static Future> _fetch() async { + final Requests req = Requests( + choreoApiKey: Environment.choreoApiKey, + ); + + final Response res = await req.get( + url: PApiUrls.getLanguages, + ); + + if (res.statusCode != 200) { + throw Exception( + 'Failed to fetch languages: ${res.statusCode} ${res.reasonPhrase}', + ); + } + + return (jsonDecode(utf8.decode(res.bodyBytes)) as List) + .map((e) { + try { + return LanguageModel.fromJson(e); + } catch (err, stack) { + debugger(when: kDebugMode); + ErrorHandler.logError(e: err, s: stack, data: e); + return null; + } + }) + .whereType() + .toList(); + } +} diff --git a/lib/pangea/learning_settings/controllers/language_controller.dart b/lib/pangea/languages/language_service.dart similarity index 72% rename from lib/pangea/learning_settings/controllers/language_controller.dart rename to lib/pangea/languages/language_service.dart index f9d57b8e3..685322937 100644 --- a/lib/pangea/learning_settings/controllers/language_controller.dart +++ b/lib/pangea/languages/language_service.dart @@ -4,12 +4,12 @@ import 'package:flutter/material.dart'; import 'package:universal_io/io.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/pangea/languages/p_language_store.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import '../widgets/p_language_dialog.dart'; +import '../learning_settings/p_language_dialog.dart'; -class LanguageController { +class LanguageService { static void showDialogOnEmptyLanguage( BuildContext context, Function callback, diff --git a/lib/pangea/learning_settings/utils/locale_provider.dart b/lib/pangea/languages/locale_provider.dart similarity index 100% rename from lib/pangea/learning_settings/utils/locale_provider.dart rename to lib/pangea/languages/locale_provider.dart diff --git a/lib/pangea/languages/p_language_store.dart b/lib/pangea/languages/p_language_store.dart new file mode 100644 index 000000000..c316f2b8c --- /dev/null +++ b/lib/pangea/languages/p_language_store.dart @@ -0,0 +1,145 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:collection/collection.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'package:fluffychat/pangea/languages/language_constants.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_repo.dart'; + +class PrefKey { + static const lastFetched = 'p_lang_lastfetched'; + static const languagesKey = 'p_lang_flag'; +} + +class PLanguageStore { + PLanguageStore() { + initialize(); + } + + static List _langList = []; + + List get targetOptions => + _langList.where((element) => element.l2).toList(); + + List get baseOptions => _langList.toList(); + + List get unlocalizedTargetOptions => _langList + .where( + (element) => + element.l2 && + (element.langCode == element.langCodeShort || + !element.displayName.contains("(")), + ) + .toList(); + + static Future initialize({forceRefresh = false}) async { + _langList = await _getCachedLanguages(); + final isOutdated = await _shouldFetch; + final shouldFetch = forceRefresh || + isOutdated || + _langList.isEmpty || + _langList.every((lang) => !lang.l2); + + if (shouldFetch) { + final result = await LanguageRepo.get(); + _langList = result.isValue + ? result.asValue!.value + : LanguageConstants.languageList + .map((e) => LanguageModel.fromJson(e)) + .toList(); + } + + await _MyShared.saveJson(PrefKey.languagesKey, { + PrefKey.languagesKey: _langList.map((e) => e.toJson()).toList(), + }); + + await _MyShared.saveString( + PrefKey.lastFetched, + DateTime.now().toIso8601String(), + ); + + _langList.removeWhere( + (element) => element.langCode == LanguageKeys.unknownLanguage, + ); + _langList = _langList.toSet().toList(); + _langList.sort((a, b) => a.displayName.compareTo(b.displayName)); + } + + static Future get _shouldFetch async { + final String? dateString = await _MyShared.readString(PrefKey.lastFetched); + if (dateString == null) { + return true; + } + + final DateTime? lastFetchedDate = DateTime.tryParse(dateString); + if (lastFetchedDate == null) { + return true; + } + + final DateTime targetDate = DateTime(2025, 2, 26); + if (lastFetchedDate.isBefore(targetDate)) { + return true; + } + + final int lastFetched = lastFetchedDate.millisecondsSinceEpoch; + final int now = DateTime.now().millisecondsSinceEpoch; + const int fetchIntervalInMilliseconds = 86534601; + return (now - lastFetched) >= fetchIntervalInMilliseconds; + } + + static Future> _getCachedLanguages() async { + final Map? languagesMap = await _MyShared.readJson( + PrefKey.languagesKey, + ); + + if (languagesMap == null) return []; + try { + return (languagesMap[PrefKey.languagesKey] as List) + .map((e) => LanguageModel.fromJson(e)) + .toList(); + } catch (err) { + return []; + } + } + + static LanguageModel? byLangCode(String langCode) => + _langList.firstWhereOrNull( + (element) => element.langCode == langCode, + ); +} + +class _MyShared { + static saveString(String key, String value) async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.setString(key, value); + } + + static Future? readString(String key) async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + final String? source = prefs.getString(key); + return source; + } + + static saveJson(String key, Map value) async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.setString(key, json.encode(value)); + } + + static Future? readJson(String key) async { + try { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + final String? source = prefs.getString(key); + + if (source == null) { + return null; + } + final decodedJson = json.decoder.convert(source); + //var decodedJson = json.decode(source); + return decodedJson; + } catch (err) { + return null; + } + } +} diff --git a/lib/pangea/learning_settings/utils/country_display.dart b/lib/pangea/learning_settings/country_display.dart similarity index 100% rename from lib/pangea/learning_settings/utils/country_display.dart rename to lib/pangea/learning_settings/country_display.dart diff --git a/lib/pangea/learning_settings/widgets/country_picker_tile.dart b/lib/pangea/learning_settings/country_picker_tile.dart similarity index 96% rename from lib/pangea/learning_settings/widgets/country_picker_tile.dart rename to lib/pangea/learning_settings/country_picker_tile.dart index 96f1c0658..250b4e6ba 100644 --- a/lib/pangea/learning_settings/widgets/country_picker_tile.dart +++ b/lib/pangea/learning_settings/country_picker_tile.dart @@ -6,8 +6,8 @@ import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; -import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/country_display.dart'; +import 'package:fluffychat/pangea/learning_settings/country_display.dart'; +import 'package:fluffychat/pangea/learning_settings/settings_learning.dart'; import 'package:fluffychat/widgets/matrix.dart'; class CountryPickerDropdown extends StatefulWidget { diff --git a/lib/pangea/learning_settings/enums/language_level_type_enum.dart b/lib/pangea/learning_settings/language_level_type_enum.dart similarity index 96% rename from lib/pangea/learning_settings/enums/language_level_type_enum.dart rename to lib/pangea/learning_settings/language_level_type_enum.dart index e44a75a22..9910819b3 100644 --- a/lib/pangea/learning_settings/enums/language_level_type_enum.dart +++ b/lib/pangea/learning_settings/language_level_type_enum.dart @@ -2,9 +2,15 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/l10n/l10n.dart'; -enum LanguageLevelTypeEnum { preA1, a1, a2, b1, b2, c1, c2 } +enum LanguageLevelTypeEnum { + preA1, + a1, + a2, + b1, + b2, + c1, + c2; -extension LanguageLevelTypeEnumExtension on LanguageLevelTypeEnum { // Makes enum a string String get string { switch (this) { diff --git a/lib/pangea/learning_settings/widgets/language_mismatch_popup.dart b/lib/pangea/learning_settings/language_mismatch_popup.dart similarity index 100% rename from lib/pangea/learning_settings/widgets/language_mismatch_popup.dart rename to lib/pangea/learning_settings/language_mismatch_popup.dart diff --git a/lib/pangea/learning_settings/repo/language_mismatch_repo.dart b/lib/pangea/learning_settings/language_mismatch_repo.dart similarity index 100% rename from lib/pangea/learning_settings/repo/language_mismatch_repo.dart rename to lib/pangea/learning_settings/language_mismatch_repo.dart diff --git a/lib/pangea/learning_settings/widgets/p_language_dialog.dart b/lib/pangea/learning_settings/p_language_dialog.dart similarity index 91% rename from lib/pangea/learning_settings/widgets/p_language_dialog.dart rename to lib/pangea/learning_settings/p_language_dialog.dart index 49598c587..92a79d816 100644 --- a/lib/pangea/learning_settings/widgets/p_language_dialog.dart +++ b/lib/pangea/learning_settings/p_language_dialog.dart @@ -6,13 +6,13 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; -import 'package:fluffychat/pangea/learning_settings/controllers/language_controller.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; +import 'package:fluffychat/pangea/languages/language_constants.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_service.dart'; +import 'package:fluffychat/pangea/languages/p_language_store.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; -import '../../../config/themes.dart'; -import '../../../widgets/matrix.dart'; +import '../../config/themes.dart'; +import '../../widgets/matrix.dart'; import 'p_language_dropdown.dart'; import 'p_question_container.dart'; @@ -24,7 +24,7 @@ Future pLanguageDialog( //PTODO: if source language not set by user, default to languge from device settings final LanguageModel? userL1 = pangeaController.userController.userL1; final LanguageModel? userL2 = pangeaController.userController.userL2; - final LanguageModel? systemLanguage = LanguageController.systemLanguage; + final LanguageModel? systemLanguage = LanguageService.systemLanguage; LanguageModel? selectedSourceLanguage = systemLanguage; if (userL1 != null && userL1.langCode != LanguageKeys.unknownLanguage) { diff --git a/lib/pangea/learning_settings/widgets/p_language_dropdown.dart b/lib/pangea/learning_settings/p_language_dropdown.dart similarity index 96% rename from lib/pangea/learning_settings/widgets/p_language_dropdown.dart rename to lib/pangea/learning_settings/p_language_dropdown.dart index 667af16f2..fa291d235 100644 --- a/lib/pangea/learning_settings/widgets/p_language_dropdown.dart +++ b/lib/pangea/learning_settings/p_language_dropdown.dart @@ -6,9 +6,9 @@ import 'package:flutter/material.dart'; import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:fluffychat/config/themes.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/l2_support_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; -import 'flag.dart'; +import 'package:fluffychat/pangea/languages/l2_support_enum.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/widgets/avatar.dart'; class PLanguageDropdown extends StatefulWidget { final List languages; @@ -210,8 +210,9 @@ class LanguageDropDownEntry extends StatelessWidget { children: [ Opacity( opacity: enabled ? 1 : 0.5, - child: LanguageFlag( - language: languageModel, + child: Avatar( + name: languageModel.langCode, + size: 30, ), ), const SizedBox(width: 10), diff --git a/lib/pangea/learning_settings/widgets/p_question_container.dart b/lib/pangea/learning_settings/p_question_container.dart similarity index 100% rename from lib/pangea/learning_settings/widgets/p_question_container.dart rename to lib/pangea/learning_settings/p_question_container.dart diff --git a/lib/pangea/learning_settings/widgets/p_settings_switch_list_tile.dart b/lib/pangea/learning_settings/p_settings_switch_list_tile.dart similarity index 100% rename from lib/pangea/learning_settings/widgets/p_settings_switch_list_tile.dart rename to lib/pangea/learning_settings/p_settings_switch_list_tile.dart diff --git a/lib/pangea/learning_settings/repo/language_repo.dart b/lib/pangea/learning_settings/repo/language_repo.dart deleted file mode 100644 index 52806e6a0..000000000 --- a/lib/pangea/learning_settings/repo/language_repo.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'dart:convert'; -import 'dart:developer'; - -import 'package:flutter/foundation.dart'; - -import 'package:http/http.dart'; - -import 'package:fluffychat/pangea/common/config/environment.dart'; -import 'package:fluffychat/pangea/common/network/urls.dart'; -import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; -import '../../common/network/requests.dart'; - -class LanguageRepo { - static Future> fetchLanguages() async { - final Requests req = Requests( - choreoApiKey: Environment.choreoApiKey, - ); - - List languageResp = []; - try { - final Response res = await req.get(url: PApiUrls.getLanguages); - languageResp = jsonDecode(utf8.decode(res.bodyBytes).toString()) as List; - } catch (e) { - languageResp = FallbackLanguage.languageList; - } - - final List langFlag = languageResp.map((e) { - try { - return LanguageModel.fromJson(e); - } catch (err, stack) { - debugger(when: kDebugMode); - ErrorHandler.logError(e: err, s: stack, data: e); - return LanguageModel.unknown; - } - }).toList(); - return langFlag; - } -} diff --git a/lib/pangea/learning_settings/pages/settings_learning.dart b/lib/pangea/learning_settings/settings_learning.dart similarity index 74% rename from lib/pangea/learning_settings/pages/settings_learning.dart rename to lib/pangea/learning_settings/settings_learning.dart index 58bfde9f2..5c5369003 100644 --- a/lib/pangea/learning_settings/pages/settings_learning.dart +++ b/lib/pangea/learning_settings/settings_learning.dart @@ -1,20 +1,23 @@ import 'dart:developer'; +import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:app_settings/app_settings.dart'; import 'package:country_picker/country_picker.dart'; +import 'package:url_launcher/url_launcher_string.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/instructions/instruction_settings.dart'; -import 'package:fluffychat/pangea/learning_settings/controllers/language_controller.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/tool_settings_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; -import 'package:fluffychat/pangea/learning_settings/pages/settings_learning_view.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_service.dart'; +import 'package:fluffychat/pangea/languages/p_language_store.dart'; +import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dart'; +import 'package:fluffychat/pangea/learning_settings/settings_learning_view.dart'; +import 'package:fluffychat/pangea/learning_settings/tool_settings_enum.dart'; import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; import 'package:fluffychat/pangea/user/models/user_model.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; @@ -204,6 +207,76 @@ class SettingsLearningController extends State { if (mounted) setState(() {}); } + void showKeyboardSettingsDialog() { + String title; + String? steps; + String? description; + String buttonText; + VoidCallback buttonAction; + + if (kIsWeb) { + title = L10n.of(context).autocorrectNotAvailable; // Default + buttonText = 'OK'; + buttonAction = () { + Navigator.of(context).pop(); + }; + } else if (Platform.isIOS) { + title = L10n.of(context).enableAutocorrectPopupTitle; + steps = L10n.of(context).enableAutocorrectPopupSteps; + description = L10n.of(context).enableAutocorrectPopupDescription; + buttonText = L10n.of(context).settings; + buttonAction = () { + AppSettings.openAppSettings(); + }; + } else { + title = L10n.of(context).downloadGboardTitle; + steps = L10n.of(context).downloadGboardSteps; + description = L10n.of(context).downloadGboardDescription; + buttonText = L10n.of(context).downloadGboard; + buttonAction = () { + launchUrlString( + 'https://play.google.com/store/apps/details?id=com.google.android.inputmethod.latin', + ); + }; + } + + showAdaptiveDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog.adaptive( + title: Text(L10n.of(context).enableAutocorrectWarning), + content: SingleChildScrollView( + child: Column( + spacing: 8.0, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(title), + if (steps != null) + Text( + steps, + textAlign: TextAlign.start, + ), + if (description != null) Text(description), + ], + ), + ), + actions: [ + TextButton( + child: Text(L10n.of(context).close), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + onPressed: buttonAction, + child: Text(buttonText), + ), + ], + ); + }, + ); + } + LanguageModel? get _targetLanguage => _profile.userSettings.targetLanguage != null ? PLanguageStore.byLangCode(_profile.userSettings.targetLanguage!) @@ -235,7 +308,7 @@ class SettingsLearningController extends State { _profile.userSettings.targetLanguage != null && _targetLanguage != null; LanguageModel? get selectedSourceLanguage { - return _selectedBaseLanguage ?? LanguageController.systemLanguage; + return _selectedBaseLanguage ?? LanguageService.systemLanguage; } LanguageModel? get selectedTargetLanguage { diff --git a/lib/pangea/learning_settings/pages/settings_learning_view.dart b/lib/pangea/learning_settings/settings_learning_view.dart similarity index 81% rename from lib/pangea/learning_settings/pages/settings_learning_view.dart rename to lib/pangea/learning_settings/settings_learning_view.dart index 0b7d32014..e5c062e31 100644 --- a/lib/pangea/learning_settings/pages/settings_learning_view.dart +++ b/lib/pangea/learning_settings/settings_learning_view.dart @@ -1,11 +1,5 @@ -import 'dart:io'; - -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:app_settings/app_settings.dart'; -import 'package:url_launcher/url_launcher_string.dart'; - import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; @@ -13,12 +7,12 @@ import 'package:fluffychat/pangea/chat_settings/widgets/language_level_dropdown. import 'package:fluffychat/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart'; import 'package:fluffychat/pangea/instructions/reset_instructions_list_tile.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/tool_settings_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; -import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart'; -import 'package:fluffychat/pangea/learning_settings/widgets/country_picker_tile.dart'; -import 'package:fluffychat/pangea/learning_settings/widgets/p_language_dropdown.dart'; -import 'package:fluffychat/pangea/learning_settings/widgets/p_settings_switch_list_tile.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/pangea/learning_settings/country_picker_tile.dart'; +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/widgets/layouts/max_width_body.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -26,76 +20,6 @@ class SettingsLearningView extends StatelessWidget { final SettingsLearningController controller; const SettingsLearningView(this.controller, {super.key}); - void _showKeyboardSettingsDialog(BuildContext context) { - String title; - String? steps; - String? description; - String buttonText; - VoidCallback buttonAction; - - if (kIsWeb) { - title = L10n.of(context).autocorrectNotAvailable; // Default - buttonText = 'OK'; - buttonAction = () { - Navigator.of(context).pop(); - }; - } else if (Platform.isIOS) { - title = L10n.of(context).enableAutocorrectPopupTitle; - steps = L10n.of(context).enableAutocorrectPopupSteps; - description = L10n.of(context).enableAutocorrectPopupDescription; - buttonText = L10n.of(context).settings; - buttonAction = () { - AppSettings.openAppSettings(); - }; - } else { - title = L10n.of(context).downloadGboardTitle; - steps = L10n.of(context).downloadGboardSteps; - description = L10n.of(context).downloadGboardDescription; - buttonText = L10n.of(context).downloadGboard; - buttonAction = () { - launchUrlString( - 'https://play.google.com/store/apps/details?id=com.google.android.inputmethod.latin', - ); - }; - } - - showAdaptiveDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog.adaptive( - title: Text(L10n.of(context).enableAutocorrectWarning), - content: SingleChildScrollView( - child: Column( - spacing: 8.0, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text(title), - if (steps != null) - Text( - steps, - textAlign: TextAlign.start, - ), - if (description != null) Text(description), - ], - ), - ), - actions: [ - TextButton( - child: Text(L10n.of(context).close), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - TextButton( - onPressed: buttonAction, - child: Text(buttonText), - ), - ], - ); - }, - ); - } - @override Widget build(BuildContext context) { return StreamBuilder( @@ -255,9 +179,8 @@ class SettingsLearningView extends StatelessWidget { value, ); if (value) { - _showKeyboardSettingsDialog( - context, - ); + controller + .showKeyboardSettingsDialog(); } }, enabled: true, diff --git a/lib/pangea/learning_settings/enums/tool_settings_enum.dart b/lib/pangea/learning_settings/tool_settings_enum.dart similarity index 100% rename from lib/pangea/learning_settings/enums/tool_settings_enum.dart rename to lib/pangea/learning_settings/tool_settings_enum.dart diff --git a/lib/pangea/learning_settings/utils/p_language_store.dart b/lib/pangea/learning_settings/utils/p_language_store.dart deleted file mode 100644 index 9c0d97863..000000000 --- a/lib/pangea/learning_settings/utils/p_language_store.dart +++ /dev/null @@ -1,133 +0,0 @@ -import 'dart:async'; -import 'dart:developer'; - -import 'package:flutter/foundation.dart'; - -import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; -import 'package:fluffychat/pangea/learning_settings/repo/language_repo.dart'; -import 'shared_prefs.dart'; - -class PLanguageStore { - PLanguageStore() { - initialize(); - } - - static List _langList = []; - - List get langList => _langList; - - List get targetOptions => - _langList.where((element) => element.l2).toList(); - - List get unlocalizedTargetOptions => _langList - .where( - (element) => - element.l2 && - (element.langCode == element.langCodeShort || - !element.displayName.contains("(")), - ) - .toList(); - - List get baseOptions => _langList.toList(); - - static Future initialize({forceRefresh = false}) async { - try { - _langList = await _getCachedLanguages(); - if (forceRefresh || - await _shouldFetch || - _langList.isEmpty || - _langList.every((lang) => !lang.l2)) { - _langList = await LanguageRepo.fetchLanguages(); - - await _saveLanguages(_langList); - await saveLastFetchDate(); - } - _langList.removeWhere( - (element) => element.langCode == LanguageKeys.unknownLanguage, - ); - // remove any duplicates - _langList = _langList.toSet().toList(); - - _langList.sort((a, b) => a.displayName.compareTo(b.displayName)); - } catch (err, stack) { - debugger(when: kDebugMode); - ErrorHandler.logError( - e: err, - s: stack, - data: { - "langList": _langList.map((e) => e.toJson()), - }, - ); - } - } - - static saveLastFetchDate() async { - final String now = DateTime.now().toIso8601String(); - await MyShared.saveString(PrefKey.lastFetched, now); - } - - static Future get _shouldFetch async { - final String? dateString = await MyShared.readString(PrefKey.lastFetched); - if (dateString == null) { - return true; - } - // return true; - final DateTime lastFetchedDate = DateTime.parse(dateString); - final DateTime targetDate = DateTime(2025, 2, 26); - if (lastFetchedDate.isBefore(targetDate)) { - return true; - } - - final int lastFetched = DateTime.parse(dateString).millisecondsSinceEpoch; - final int now = DateTime.now().millisecondsSinceEpoch; - const int fetchIntervalInMilliseconds = 86534601; - return (now - lastFetched) >= fetchIntervalInMilliseconds ? true : false; - } - - static Future _saveLanguages(List languages) async { - final Map languagesMaps = { - PrefKey.languagesKey: languages.map((e) => e.toJson()).toList(), - }; - await MyShared.saveJson(PrefKey.languagesKey, languagesMaps); - } - - static Future> _getCachedLanguages() async { - final Map? languagesMap = - await MyShared.readJson(PrefKey.languagesKey); - if (languagesMap == null) { - return []; - } - - final List languages = []; - final List mapList = languagesMap[PrefKey.languagesKey] as List; - for (final element in mapList) { - languages.add(LanguageModel.fromJson(element)); - } - - return languages; - } - - static LanguageModel? byLangCode(String langCode) { - for (final element in _langList) { - if (element.langCode == langCode) return element; - } - return null; - } - - static final List rtlLanguageCodes = [ - 'ar', - 'arc', - 'dv', - 'fa', - 'ha', - 'he', - 'khw', - 'ks', - 'ku', - 'ps', - 'ur', - 'yi', - ]; -} diff --git a/lib/pangea/learning_settings/utils/shared_prefs.dart b/lib/pangea/learning_settings/utils/shared_prefs.dart deleted file mode 100644 index 3d22a9971..000000000 --- a/lib/pangea/learning_settings/utils/shared_prefs.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'dart:convert'; - -import 'package:shared_preferences/shared_preferences.dart'; - -class MyShared { - static saveString(String key, String value) async { - final SharedPreferences prefs = await SharedPreferences.getInstance(); - prefs.setString(key, value); - } - - static Future? readString(String key) async { - final SharedPreferences prefs = await SharedPreferences.getInstance(); - final String? source = prefs.getString(key); - return source; - } - - static saveJson(String key, Map value) async { - final SharedPreferences prefs = await SharedPreferences.getInstance(); - prefs.setString(key, json.encode(value)); - } - - static Future? readJson(String key) async { - try { - final SharedPreferences prefs = await SharedPreferences.getInstance(); - final String? source = prefs.getString(key); - - if (source == null) { - return null; - } - final decodedJson = json.decoder.convert(source); - //var decodedJson = json.decode(source); - return decodedJson; - } catch (err) { - return null; - } - } -} diff --git a/lib/pangea/learning_settings/widgets/flag.dart b/lib/pangea/learning_settings/widgets/flag.dart deleted file mode 100644 index 0b8e6526b..000000000 --- a/lib/pangea/learning_settings/widgets/flag.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:fluffychat/widgets/avatar.dart'; -import '../models/language_model.dart'; - -class LanguageFlag extends StatelessWidget { - final LanguageModel? language; - final double size; - const LanguageFlag({ - super.key, - required this.language, - this.size = 30, - }); - - @override - Widget build(BuildContext context) { - return Avatar( - name: language?.langCode, - size: size, - ); - } -} diff --git a/lib/pangea/login/pages/create_pangea_account_page.dart b/lib/pangea/login/pages/create_pangea_account_page.dart index 2cb04d403..599866b76 100644 --- a/lib/pangea/login/pages/create_pangea_account_page.dart +++ b/lib/pangea/login/pages/create_pangea_account_page.dart @@ -13,8 +13,8 @@ import 'package:fluffychat/pangea/common/widgets/error_indicator.dart'; import 'package:fluffychat/pangea/course_plans/courses/course_plan_room_extension.dart'; import 'package:fluffychat/pangea/course_plans/courses/course_plans_repo.dart'; import 'package:fluffychat/pangea/course_plans/courses/get_localized_courses_request.dart'; -import 'package:fluffychat/pangea/learning_settings/controllers/language_controller.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; +import 'package:fluffychat/pangea/languages/language_service.dart'; +import 'package:fluffychat/pangea/languages/p_language_store.dart'; import 'package:fluffychat/pangea/login/utils/lang_code_repo.dart'; import 'package:fluffychat/pangea/spaces/space_code_controller.dart'; import 'package:fluffychat/pangea/spaces/space_code_repo.dart'; @@ -53,7 +53,7 @@ class CreatePangeaAccountPageState extends State { Future get _baseLangCode async => (await _cachedLangCode)?.baseLangCode ?? - LanguageController.systemLanguage?.langCode; + LanguageService.systemLanguage?.langCode; String? get _cachedSpaceCode => SpaceCodeRepo.spaceCode; @@ -188,7 +188,7 @@ class CreatePangeaAccountPageState extends State { if (targetLangCode != null) MatrixState.pangeaController.userController.updateAnalyticsProfile( targetLanguage: PLanguageStore.byLangCode(targetLangCode), - baseLanguage: LanguageController.systemLanguage, + baseLanguage: LanguageService.systemLanguage, level: 1, ), ]; diff --git a/lib/pangea/login/pages/language_selection_page.dart b/lib/pangea/login/pages/language_selection_page.dart index 652d9e86c..eb54b0325 100644 --- a/lib/pangea/login/pages/language_selection_page.dart +++ b/lib/pangea/login/pages/language_selection_page.dart @@ -4,10 +4,10 @@ import 'package:go_router/go_router.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/learning_settings/controllers/language_controller.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; -import 'package:fluffychat/pangea/learning_settings/widgets/p_language_dropdown.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_service.dart'; +import 'package:fluffychat/pangea/languages/p_language_store.dart'; +import 'package:fluffychat/pangea/learning_settings/p_language_dropdown.dart'; import 'package:fluffychat/pangea/login/utils/lang_code_repo.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -31,7 +31,7 @@ class LanguageSelectionPageState extends State { @override void initState() { super.initState(); - _baseLanguage = LanguageController.systemLanguage; + _baseLanguage = LanguageService.systemLanguage; _setFromCache(); } diff --git a/lib/pangea/login/pages/new_course_page.dart b/lib/pangea/login/pages/new_course_page.dart index 44585f51c..65903c224 100644 --- a/lib/pangea/login/pages/new_course_page.dart +++ b/lib/pangea/login/pages/new_course_page.dart @@ -16,8 +16,8 @@ import 'package:fluffychat/pangea/course_plans/courses/course_plan_client_extens import 'package:fluffychat/pangea/course_plans/courses/course_plan_model.dart'; import 'package:fluffychat/pangea/course_plans/courses/course_plans_repo.dart'; import 'package:fluffychat/pangea/course_plans/courses/get_localized_courses_response.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; diff --git a/lib/pangea/login/pages/public_courses_page.dart b/lib/pangea/login/pages/public_courses_page.dart index b64c83e3b..d56ec291b 100644 --- a/lib/pangea/login/pages/public_courses_page.dart +++ b/lib/pangea/login/pages/public_courses_page.dart @@ -12,7 +12,7 @@ import 'package:fluffychat/pangea/course_creation/course_language_filter.dart'; import 'package:fluffychat/pangea/course_plans/courses/course_plan_model.dart'; import 'package:fluffychat/pangea/course_plans/courses/course_plans_repo.dart'; import 'package:fluffychat/pangea/course_plans/courses/get_localized_courses_request.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; import 'package:fluffychat/pangea/spaces/utils/public_course_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; diff --git a/lib/pangea/morphs/morph_meaning/morph_info_repo.dart b/lib/pangea/morphs/morph_meaning/morph_info_repo.dart index 76ff693f0..3a0a0d900 100644 --- a/lib/pangea/morphs/morph_meaning/morph_info_repo.dart +++ b/lib/pangea/morphs/morph_meaning/morph_info_repo.dart @@ -8,7 +8,7 @@ import 'package:http/http.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; import 'package:fluffychat/pangea/common/network/requests.dart'; import 'package:fluffychat/pangea/common/network/urls.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; +import 'package:fluffychat/pangea/languages/language_constants.dart'; import 'package:fluffychat/pangea/morphs/morph_features_enum.dart'; import 'package:fluffychat/pangea/morphs/morph_meaning/morph_info_request.dart'; import 'package:fluffychat/pangea/morphs/morph_meaning/morph_info_response.dart'; diff --git a/lib/pangea/morphs/morph_repo.dart b/lib/pangea/morphs/morph_repo.dart index 03867a4f0..40f5d0a37 100644 --- a/lib/pangea/morphs/morph_repo.dart +++ b/lib/pangea/morphs/morph_repo.dart @@ -9,7 +9,7 @@ import 'package:http/http.dart'; import 'package:fluffychat/pangea/common/config/environment.dart'; import 'package:fluffychat/pangea/common/network/urls.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; import 'package:fluffychat/pangea/morphs/default_morph_mapping.dart'; import 'package:fluffychat/pangea/morphs/morph_models.dart'; import 'package:fluffychat/widgets/matrix.dart'; diff --git a/lib/pangea/phonetic_transcription/phonetic_transcription_request.dart b/lib/pangea/phonetic_transcription/phonetic_transcription_request.dart index cbd22fc22..be229cba1 100644 --- a/lib/pangea/phonetic_transcription/phonetic_transcription_request.dart +++ b/lib/pangea/phonetic_transcription/phonetic_transcription_request.dart @@ -1,5 +1,5 @@ import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_arc_model.dart'; class PhoneticTranscriptionRequest { final LanguageArc arc; diff --git a/lib/pangea/phonetic_transcription/phonetic_transcription_response.dart b/lib/pangea/phonetic_transcription/phonetic_transcription_response.dart index a4cd2b3a3..0f9a62b43 100644 --- a/lib/pangea/phonetic_transcription/phonetic_transcription_response.dart +++ b/lib/pangea/phonetic_transcription/phonetic_transcription_response.dart @@ -1,5 +1,5 @@ import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_arc_model.dart'; enum PhoneticTranscriptionDelimEnum { sp, noSp } diff --git a/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart b/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart index dcf3bc338..ae7b533b1 100644 --- a/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart +++ b/lib/pangea/phonetic_transcription/phonetic_transcription_widget.dart @@ -7,7 +7,8 @@ import 'package:fluffychat/pangea/common/network/requests.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/common/widgets/error_indicator.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_arc_model.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_repo.dart'; import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_request.dart'; import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; diff --git a/lib/pangea/space_analytics/analytics_requests_repo.dart b/lib/pangea/space_analytics/analytics_requests_repo.dart index afc0ed385..ef6bfe1a5 100644 --- a/lib/pangea/space_analytics/analytics_requests_repo.dart +++ b/lib/pangea/space_analytics/analytics_requests_repo.dart @@ -1,6 +1,6 @@ import 'package:get_storage/get_storage.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; import 'package:fluffychat/pangea/space_analytics/space_analytics_download_enum.dart'; class _AnalyticsRequestEntry { diff --git a/lib/pangea/space_analytics/space_analytics.dart b/lib/pangea/space_analytics/space_analytics.dart index a444796b8..1f007d196 100644 --- a/lib/pangea/space_analytics/space_analytics.dart +++ b/lib/pangea/space_analytics/space_analytics.dart @@ -10,8 +10,8 @@ import 'package:fluffychat/pangea/analytics_misc/construct_list_model.dart'; import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart'; import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/pangea/languages/p_language_store.dart'; import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart'; import 'package:fluffychat/pangea/space_analytics/analytics_download_model.dart'; import 'package:fluffychat/pangea/space_analytics/analytics_requests_repo.dart'; diff --git a/lib/pangea/space_analytics/space_analytics_view.dart b/lib/pangea/space_analytics/space_analytics_view.dart index 91a7daf7b..23e9fe7e5 100644 --- a/lib/pangea/space_analytics/space_analytics_view.dart +++ b/lib/pangea/space_analytics/space_analytics_view.dart @@ -7,7 +7,7 @@ import 'package:material_symbols_icons/symbols.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/widgets/dropdown_text_button.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; import 'package:fluffychat/pangea/space_analytics/download_space_analytics_dialog.dart'; import 'package:fluffychat/pangea/space_analytics/space_analytics.dart'; import 'package:fluffychat/pangea/space_analytics/space_analytics_download_enum.dart'; diff --git a/lib/pangea/text_to_speech/tts_controller.dart b/lib/pangea/text_to_speech/tts_controller.dart index 4c2676bad..a67865316 100644 --- a/lib/pangea/text_to_speech/tts_controller.dart +++ b/lib/pangea/text_to_speech/tts_controller.dart @@ -19,7 +19,7 @@ import 'package:fluffychat/pangea/common/utils/overlay.dart'; import 'package:fluffychat/pangea/common/widgets/card_header.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; import 'package:fluffychat/pangea/instructions/instructions_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; +import 'package:fluffychat/pangea/languages/language_constants.dart'; import 'package:fluffychat/pangea/text_to_speech/text_to_speech_repo.dart'; import 'package:fluffychat/pangea/text_to_speech/text_to_speech_request_model.dart'; import 'package:fluffychat/pangea/text_to_speech/text_to_speech_response_model.dart'; diff --git a/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart b/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart index 95c987eda..3a4c480a0 100644 --- a/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart +++ b/lib/pangea/token_info_feedback/token_info_feedback_dialog.dart @@ -6,8 +6,8 @@ import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dar import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; +import 'package:fluffychat/pangea/languages/language_arc_model.dart'; +import 'package:fluffychat/pangea/languages/p_language_store.dart'; import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart'; import 'package:fluffychat/pangea/lemmas/user_set_lemma_info.dart'; import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_repo.dart'; diff --git a/lib/pangea/toolbar/widgets/overlay_message.dart b/lib/pangea/toolbar/widgets/overlay_message.dart index 936ebe525..59149cc6b 100644 --- a/lib/pangea/toolbar/widgets/overlay_message.dart +++ b/lib/pangea/toolbar/widgets/overlay_message.dart @@ -14,8 +14,8 @@ 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/learning_settings/models/language_model.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.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/enums/reading_assistance_mode_enum.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart'; diff --git a/lib/pangea/toolbar/widgets/word_zoom/lemma_meaning_builder.dart b/lib/pangea/toolbar/widgets/word_zoom/lemma_meaning_builder.dart index 7ca56172b..f1b1560dc 100644 --- a/lib/pangea/toolbar/widgets/word_zoom/lemma_meaning_builder.dart +++ b/lib/pangea/toolbar/widgets/word_zoom/lemma_meaning_builder.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; +import 'package:fluffychat/pangea/languages/language_constants.dart'; import 'package:fluffychat/pangea/lemmas/lemma_info_repo.dart'; import 'package:fluffychat/pangea/lemmas/lemma_info_request.dart'; import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart'; diff --git a/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart b/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart index cd0ac0d33..931097089 100644 --- a/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart +++ b/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart @@ -7,8 +7,8 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/widgets/error_indicator.dart'; import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/pangea/languages/p_language_store.dart'; import 'package:fluffychat/pangea/lemmas/lemma_reaction_picker.dart'; import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_widget.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_unsubscribed_card.dart'; diff --git a/lib/pangea/user/controllers/user_controller.dart b/lib/pangea/user/controllers/user_controller.dart index a0a81da20..7143226f0 100644 --- a/lib/pangea/user/controllers/user_controller.dart +++ b/lib/pangea/user/controllers/user_controller.dart @@ -12,11 +12,11 @@ import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; -import 'package:fluffychat/pangea/learning_settings/controllers/language_controller.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/tool_settings_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; +import 'package:fluffychat/pangea/languages/language_constants.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/pangea/languages/language_service.dart'; +import 'package:fluffychat/pangea/languages/p_language_store.dart'; +import 'package:fluffychat/pangea/learning_settings/tool_settings_enum.dart'; import 'package:fluffychat/pangea/user/models/activities_profile_model.dart'; import 'package:fluffychat/pangea/user/models/analytics_profile_model.dart'; import '../models/user_model.dart'; @@ -552,7 +552,7 @@ class UserController { String? get userL1Code { final source = profile.userSettings.sourceLanguage; return source == null || source.isEmpty - ? LanguageController.systemLanguage?.langCode + ? LanguageService.systemLanguage?.langCode : source; } diff --git a/lib/pangea/user/models/analytics_profile_model.dart b/lib/pangea/user/models/analytics_profile_model.dart index 127e7d3c9..d20aa1841 100644 --- a/lib/pangea/user/models/analytics_profile_model.dart +++ b/lib/pangea/user/models/analytics_profile_model.dart @@ -1,7 +1,7 @@ import 'package:fluffychat/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/pangea/languages/p_language_store.dart'; class AnalyticsProfileModel { LanguageModel? baseLanguage; diff --git a/lib/pangea/user/models/user_model.dart b/lib/pangea/user/models/user_model.dart index 6b89cdb1a..ee2f9557c 100644 --- a/lib/pangea/user/models/user_model.dart +++ b/lib/pangea/user/models/user_model.dart @@ -3,10 +3,10 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/instructions/instruction_settings.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; -import 'package:fluffychat/pangea/learning_settings/enums/tool_settings_enum.dart'; +import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dart'; +import 'package:fluffychat/pangea/learning_settings/tool_settings_enum.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import '../../learning_settings/models/language_model.dart'; +import '../../languages/language_model.dart'; /// The user's settings learning settings. class UserSettings { @@ -40,7 +40,7 @@ class UserSettings { sourceLanguage: json[ModelKey.l1LanguageKey], country: json[ModelKey.userCountry], cefrLevel: json[ModelKey.cefrLevel] is String - ? LanguageLevelTypeEnumExtension.fromString( + ? LanguageLevelTypeEnum.fromString( json[ModelKey.cefrLevel], ) : LanguageLevelTypeEnum.a1, diff --git a/lib/pangea/word_bank/vocab_bank_repo.dart b/lib/pangea/word_bank/vocab_bank_repo.dart index a1e9404f0..0373f7072 100644 --- a/lib/pangea/word_bank/vocab_bank_repo.dart +++ b/lib/pangea/word_bank/vocab_bank_repo.dart @@ -12,9 +12,9 @@ import 'package:fluffychat/pangea/common/network/requests.dart'; import 'package:fluffychat/pangea/common/network/urls.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; -import 'package:fluffychat/pangea/learning_settings/models/language_model.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart'; +import 'package:fluffychat/pangea/languages/language_constants.dart'; +import 'package:fluffychat/pangea/languages/language_model.dart'; +import 'package:fluffychat/pangea/languages/p_language_store.dart'; import 'package:fluffychat/pangea/word_bank/vocab_request.dart'; import 'package:fluffychat/pangea/word_bank/vocab_response.dart'; import 'package:fluffychat/widgets/matrix.dart'; diff --git a/lib/pangea/word_bank/vocab_request.dart b/lib/pangea/word_bank/vocab_request.dart index df2b14450..06935d152 100644 --- a/lib/pangea/word_bank/vocab_request.dart +++ b/lib/pangea/word_bank/vocab_request.dart @@ -1,4 +1,4 @@ -import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart'; +import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dart'; class VocabRequest { String langCode; diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart index 5b803ebc7..0579ea92c 100644 --- a/lib/utils/background_push.dart +++ b/lib/utils/background_push.dart @@ -34,7 +34,7 @@ import 'package:unifiedpush_ui/unifiedpush_ui.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; +import 'package:fluffychat/pangea/languages/language_constants.dart'; import 'package:fluffychat/utils/push_helper.dart'; import 'package:fluffychat/widgets/fluffy_chat_app.dart'; import '../config/app_config.dart'; diff --git a/lib/widgets/fluffy_chat_app.dart b/lib/widgets/fluffy_chat_app.dart index 12045d233..aa773d5d5 100644 --- a/lib/widgets/fluffy_chat_app.dart +++ b/lib/widgets/fluffy_chat_app.dart @@ -11,7 +11,7 @@ import 'package:fluffychat/config/routes.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/utils/firebase_analytics.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/locale_provider.dart'; +import 'package:fluffychat/pangea/languages/locale_provider.dart'; import 'package:fluffychat/widgets/app_lock.dart'; import 'package:fluffychat/widgets/theme_builder.dart'; import '../config/app_config.dart'; diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index ec036889e..408747c54 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -23,7 +23,7 @@ import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/any_state_holder.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/learning_settings/utils/locale_provider.dart'; +import 'package:fluffychat/pangea/languages/locale_provider.dart'; import 'package:fluffychat/pangea/spaces/space_code_controller.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dart'; From 25bae8baf67be7416b9943a433846db5b0e201b8 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 4 Dec 2025 10:44:04 -0500 Subject: [PATCH 8/9] remove matrix widget reference from pangea controller --- lib/pages/chat_list/chat_list.dart | 20 +- lib/pages/login/login.dart | 31 +- lib/pages/settings/settings.dart | 2 +- .../analytics_dowload_dialog.dart | 16 +- .../get_analytics_controller.dart | 4 +- .../level_up/level_up_banner.dart | 1 - .../level_up/level_up_manager.dart | 3 - .../put_analytics_controller.dart | 8 +- .../utils/bot_client_extension.dart | 19 ++ .../common/controllers/pangea_controller.dart | 303 +++++++----------- .../learning_settings/settings_learning.dart | 2 +- .../space_analytics/space_analytics.dart | 2 +- .../widgets/load_participants_builder.dart | 2 +- .../controllers/subscription_controller.dart | 2 +- .../widgets/subscription_paywall.dart | 3 +- .../widgets/message_selection_positioner.dart | 2 +- .../activities_profile_model.dart | 0 .../{models => }/analytics_profile_model.dart | 0 lib/pangea/user/{utils => }/p_login.dart | 0 lib/pangea/user/{utils => }/p_logout.dart | 0 .../user/pangea_push_rules_extension.dart | 47 +++ .../{controllers => }/user_controller.dart | 16 +- lib/pangea/user/{models => }/user_model.dart | 2 +- lib/widgets/matrix.dart | 2 +- 24 files changed, 241 insertions(+), 246 deletions(-) rename lib/pangea/user/{models => }/activities_profile_model.dart (100%) rename lib/pangea/user/{models => }/analytics_profile_model.dart (100%) rename lib/pangea/user/{utils => }/p_login.dart (100%) rename lib/pangea/user/{utils => }/p_logout.dart (100%) create mode 100644 lib/pangea/user/pangea_push_rules_extension.dart rename lib/pangea/user/{controllers => }/user_controller.dart (97%) rename lib/pangea/user/{models => }/user_model.dart (99%) diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index fa1d779ca..6d1228d06 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -42,6 +42,7 @@ import '../../widgets/matrix.dart'; import 'package:fluffychat/utils/tor_stub.dart' if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart'; + enum PopupMenuAction { settings, invite, @@ -565,8 +566,10 @@ class ChatListController extends State _checkTorBrowser(); //#Pangea - _invitedSpaceSubscription = MatrixState - .pangeaController.matrixState.client.onSync.stream + _invitedSpaceSubscription = Matrix.of(context) + .client + .onSync + .stream .where((event) => event.rooms?.invite != null) .listen((event) async { for (final inviteEntry in event.rooms!.invite!.entries) { @@ -584,10 +587,9 @@ class ChatListController extends State if (isSpace) { final spaceId = inviteEntry.key; - final space = - MatrixState.pangeaController.matrixState.client.getRoomById( - spaceId, - ); + final space = Matrix.of(context).client.getRoomById( + spaceId, + ); if (space?.classCode?.toLowerCase() == SpaceCodeRepo.recentCode?.toLowerCase()) { @@ -603,8 +605,8 @@ class ChatListController extends State } if (isAnalytics) { - final analyticsRoom = MatrixState.pangeaController.matrixState.client - .getRoomById(inviteEntry.key); + final analyticsRoom = + Matrix.of(context).client.getRoomById(inviteEntry.key); try { await analyticsRoom?.join(); } catch (err, s) { @@ -626,7 +628,7 @@ class ChatListController extends State // listen for space child updates for any space that is not the active space // so that when the user navigates to the space that was updated, it will // reload any rooms that have been added / removed - final client = MatrixState.pangeaController.matrixState.client; + final client = Matrix.of(context).client; // listen for room join events and leave room if over capacity _roomCapacitySubscription ??= client.onSync.stream diff --git a/lib/pages/login/login.dart b/lib/pages/login/login.dart index 185753e49..e737655bf 100644 --- a/lib/pages/login/login.dart +++ b/lib/pages/login/login.dart @@ -2,12 +2,12 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/login/pages/login_options_view.dart'; import 'package:fluffychat/pangea/login/pages/pangea_login_view.dart'; import 'package:fluffychat/pangea/login/widgets/p_sso_button.dart'; -import 'package:fluffychat/pangea/user/utils/p_login.dart'; +import 'package:fluffychat/pangea/user/p_login.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart'; @@ -46,7 +46,6 @@ class LoginController extends State { bool loadingAppleSSO = false; bool loadingGoogleSSO = false; - final PangeaController pangeaController = MatrixState.pangeaController; final GlobalKey formKey = GlobalKey(); Client? client; @@ -61,7 +60,7 @@ class LoginController extends State { // TODO: implement initState super.initState(); loadingSignIn = true; - pangeaController.checkHomeServerAction().then((client) { + checkHomeServerAction().then((client) { if (mounted) { setState(() { loadingSignIn = false; @@ -234,6 +233,30 @@ class LoginController extends State { // if (mounted) setState(() {}); // } // } + + Future checkHomeServerAction() async { + final client = await Matrix.of(context).getLoginClient(); + if (client.homeserver != null) { + await Future.delayed(Duration.zero); + return client; + } + + final String homeServer = + AppConfig.defaultHomeserver.trim().toLowerCase().replaceAll(' ', '-'); + var homeserver = Uri.parse(homeServer); + if (homeserver.scheme.isEmpty) { + homeserver = Uri.https(homeServer, ''); + } + + try { + await client.register(); + Matrix.of(context).loginRegistrationSupported = true; + } on MatrixException catch (e) { + Matrix.of(context).loginRegistrationSupported = + e.requireAdditionalAuthentication; + } + return client; + } // Pangea# void passwordForgotten() async { diff --git a/lib/pages/settings/settings.dart b/lib/pages/settings/settings.dart index cc58dab88..e6da2c762 100644 --- a/lib/pages/settings/settings.dart +++ b/lib/pages/settings/settings.dart @@ -5,7 +5,7 @@ import 'package:image_picker/image_picker.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/l10n/l10n.dart'; -import 'package:fluffychat/pangea/user/utils/p_logout.dart'; +import 'package:fluffychat/pangea/user/p_logout.dart'; import 'package:fluffychat/utils/file_selector.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_modal_action_popup.dart'; diff --git a/lib/pangea/analytics_downloads/analytics_dowload_dialog.dart b/lib/pangea/analytics_downloads/analytics_dowload_dialog.dart index b18cb3f8c..8674e0060 100644 --- a/lib/pangea/analytics_downloads/analytics_dowload_dialog.dart +++ b/lib/pangea/analytics_downloads/analytics_dowload_dialog.dart @@ -83,6 +83,7 @@ class AnalyticsDownloadDialogState extends State { return; } + final client = Matrix.of(context).client; try { if (_downloadType == DownloadType.csv) { final vocabContent = _getCSVFileContent( @@ -96,10 +97,10 @@ class AnalyticsDownloadDialogState extends State { ); final vocabFileName = - "analytics_vocab_${MatrixState.pangeaController.matrixState.client.userID?.localpart}_${DateFormat('yyyy-MM-dd-hh:mm:ss').format(DateTime.now())}.csv"; + "analytics_vocab_${client.userID?.localpart}_${DateFormat('yyyy-MM-dd-hh:mm:ss').format(DateTime.now())}.csv"; final morphFileName = - "analytics_morph_${MatrixState.pangeaController.matrixState.client.userID?.localpart}_${DateFormat('yyyy-MM-dd-hh:mm:ss').format(DateTime.now())}.csv"; + "analytics_morph_${client.userID?.localpart}_${DateFormat('yyyy-MM-dd-hh:mm:ss').format(DateTime.now())}.csv"; final futures = [ DownloadUtil.downloadFile( @@ -122,7 +123,7 @@ class AnalyticsDownloadDialogState extends State { }); final fileName = - "analytics_${MatrixState.pangeaController.matrixState.client.userID?.localpart}_${DateFormat('yyyy-MM-dd-hh:mm:ss').format(DateTime.now())}.xlsx'}"; + "analytics_${client.userID?.localpart}_${DateFormat('yyyy-MM-dd-hh:mm:ss').format(DateTime.now())}.xlsx'}"; await DownloadUtil.downloadFile( content, @@ -263,8 +264,8 @@ class AnalyticsDownloadDialogState extends State { final List examples = []; for (final OneConstructUse use in allUses) { if (use.metadata.roomId == null) continue; - final Room? room = MatrixState.pangeaController.matrixState.client - .getRoomById(use.metadata.roomId!); + final client = Matrix.of(context).client; + final Room? room = client.getRoomById(use.metadata.roomId!); if (room == null) continue; if (use.useType.skillsEnumType != LearningSkillsEnum.writing || @@ -287,12 +288,11 @@ class AnalyticsDownloadDialogState extends State { final Event? event = await room.getEventById(use.metadata.eventId!); - if (event == null || event.senderId != room.client.userID) continue; + if (event == null || event.senderId != client.userID) continue; final PangeaMessageEvent pangeaMessageEvent = PangeaMessageEvent( event: event, timeline: timeline!, - ownMessage: event.senderId == - MatrixState.pangeaController.matrixState.client.userID, + ownMessage: event.senderId == client.userID, ); examples.add(pangeaMessageEvent); if (examples.length >= 5) break; diff --git a/lib/pangea/analytics_misc/get_analytics_controller.dart b/lib/pangea/analytics_misc/get_analytics_controller.dart index b79c6cd4a..1e1b59742 100644 --- a/lib/pangea/analytics_misc/get_analytics_controller.dart +++ b/lib/pangea/analytics_misc/get_analytics_controller.dart @@ -379,8 +379,8 @@ class GetAnalyticsController extends BaseController { // this function gets called soon after login, so first // make sure that the user's l2 is loaded, if the user has set their l2 if (_client.userID != null && _l2 == null) { - if (_pangeaController.matrixState.client.prevBatch == null) { - await _pangeaController.matrixState.client.onSync.stream.first; + if (_client.prevBatch == null) { + await _client.onSync.stream.first; } if (_l2 == null) return null; } diff --git a/lib/pangea/analytics_misc/level_up/level_up_banner.dart b/lib/pangea/analytics_misc/level_up/level_up_banner.dart index 7947a5794..f9cb8c449 100644 --- a/lib/pangea/analytics_misc/level_up/level_up_banner.dart +++ b/lib/pangea/analytics_misc/level_up/level_up_banner.dart @@ -106,7 +106,6 @@ class LevelUpBannerState extends State _loadConstructSummary(); LevelUpManager.instance.preloadAnalytics( - context, widget.level, widget.prevLevel, ); diff --git a/lib/pangea/analytics_misc/level_up/level_up_manager.dart b/lib/pangea/analytics_misc/level_up/level_up_manager.dart index f446f24a3..d124e399d 100644 --- a/lib/pangea/analytics_misc/level_up/level_up_manager.dart +++ b/lib/pangea/analytics_misc/level_up/level_up_manager.dart @@ -1,5 +1,3 @@ -import 'package:flutter/material.dart'; - import 'package:matrix/matrix.dart'; import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart'; @@ -27,7 +25,6 @@ class LevelUpManager { bool shouldAutoPopup = false; Future preloadAnalytics( - BuildContext context, int level, int prevLevel, ) async { diff --git a/lib/pangea/analytics_misc/put_analytics_controller.dart b/lib/pangea/analytics_misc/put_analytics_controller.dart index 5b354793d..d504a86f9 100644 --- a/lib/pangea/analytics_misc/put_analytics_controller.dart +++ b/lib/pangea/analytics_misc/put_analytics_controller.dart @@ -83,7 +83,7 @@ class PutAnalyticsController extends BaseController { /// If analytics haven't been updated in the last day, update them Future _refreshAnalyticsIfOutdated() async { // don't set anything is the user is not logged in - if (_pangeaController.matrixState.client.userID == null) return; + if (_client.userID == null) return; try { // if lastUpdated hasn't been set yet, set it lastUpdated ??= @@ -359,7 +359,7 @@ class PutAnalyticsController extends BaseController { onLogout = false, LanguageModel? l2Override, }) async { - if (_pangeaController.matrixState.client.userID == null) return; + if (_client.userID == null) return; if (_pangeaController.getAnalytics.messagesSinceUpdate.isEmpty) return; if (!(_updateCompleter?.isCompleted ?? true)) { @@ -417,7 +417,7 @@ class PutAnalyticsController extends BaseController { } Future sendActivityAnalytics(String roomId) async { - if (_pangeaController.matrixState.client.userID == null) return; + if (_client.userID == null) return; if (_pangeaController.userController.userL2 == null) return; final Room? analyticsRoom = await _client.getMyAnalyticsRoom( @@ -435,7 +435,7 @@ class PutAnalyticsController extends BaseController { } Future removeActivityAnalytics(String roomId) async { - if (_pangeaController.matrixState.client.userID == null) return; + if (_client.userID == null) return; if (_pangeaController.userController.userL2 == null) return; final Room? analyticsRoom = await _client.getMyAnalyticsRoom( diff --git a/lib/pangea/chat_settings/utils/bot_client_extension.dart b/lib/pangea/chat_settings/utils/bot_client_extension.dart index 407f40f6b..e4094c827 100644 --- a/lib/pangea/chat_settings/utils/bot_client_extension.dart +++ b/lib/pangea/chat_settings/utils/bot_client_extension.dart @@ -53,4 +53,23 @@ extension BotClientExtension on Client { ), ], ); + + Future updateBotOptions() async { + if (!isLogged() || botDM == null) return; + + final targetLanguage = + MatrixState.pangeaController.userController.userL2?.langCode; + final cefrLevel = MatrixState + .pangeaController.userController.profile.userSettings.cefrLevel; + final updateBotOptions = botDM!.botOptions ?? BotOptionsModel(); + + if (updateBotOptions.targetLanguage == targetLanguage && + updateBotOptions.languageLevel == cefrLevel) { + return; + } + + updateBotOptions.targetLanguage = targetLanguage; + updateBotOptions.languageLevel = cefrLevel; + await botDM!.setBotOptions(updateBotOptions); + } } diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index f92013bfb..200eaff66 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -7,20 +7,16 @@ import 'package:matrix/matrix.dart'; import 'package:provider/provider.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/analytics_misc/get_analytics_controller.dart'; import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.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/utils/bot_client_extension.dart'; -import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; -import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/guard/p_vguard.dart'; import 'package:fluffychat/pangea/languages/locale_provider.dart'; 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/controllers/user_controller.dart'; +import 'package:fluffychat/pangea/user/pangea_push_rules_extension.dart'; +import 'package:fluffychat/pangea/user/user_controller.dart'; import 'package:fluffychat/widgets/matrix.dart'; import '../utils/firebase_analytics.dart'; @@ -38,16 +34,15 @@ class PangeaController { StreamSubscription? _settingsSubscription; ///Matrix Variables - MatrixState matrixState; - Matrix matrix; + final MatrixState matrixState; - PangeaController({required this.matrix, required this.matrixState}) { + PangeaController({required this.matrixState}) { userController = UserController(this); getAnalytics = GetAnalyticsController(this); putAnalytics = PutAnalyticsController(this); subscriptionController = SubscriptionController(this); PAuthGaurd.pController = this; - _setSettingsSubscriptions(); + _registerSubscriptions(); } /// Initializes various controllers and settings. @@ -57,17 +52,115 @@ class PangeaController { void initControllers() { _initAnalyticsControllers(); subscriptionController.initialize(); - setPangeaPushRules(); + matrixState.client.setPangeaPushRules(); TtsController.setAvailableLanguages(); } - void _logOutfromPangea(BuildContext context) { - debugPrint("Pangea logout"); + void _onLogin(BuildContext context) { + initControllers(); + _registerSubscriptions(); + + userController.reinitialize().then((_) { + final l1 = userController.profile.userSettings.sourceLanguage; + Provider.of(context, listen: false).setLocale(l1); + }); + subscriptionController.reinitialize(); + } + + void _onLogout(BuildContext context) { + _disposeAnalyticsControllers(); + userController.clear(); + _languageSubscription?.cancel(); + _settingsSubscription?.cancel(); + _languageSubscription = null; + _settingsSubscription = null; + GoogleAnalytics.logout(); - clearCache(); + _clearCache(); Provider.of(context, listen: false).setLocale(null); } + void handleLoginStateChange( + LoginState state, + String? userID, + BuildContext context, + ) { + switch (state) { + case LoginState.loggedOut: + case LoginState.softLoggedOut: + _onLogout(context); + break; + case LoginState.loggedIn: + _onLogin(context); + break; + } + + Sentry.configureScope( + (scope) => scope.setUser( + SentryUser( + id: userID, + name: userID, + ), + ), + ); + GoogleAnalytics.analyticsUserUpdate(userID); + } + + void _disposeAnalyticsControllers() { + putAnalytics.dispose(); + getAnalytics.dispose(); + } + + void _registerSubscriptions() { + _languageSubscription?.cancel(); + _languageSubscription = + userController.languageStream.stream.listen(_onLanguageUpdate); + + _settingsSubscription?.cancel(); + _settingsSubscription = userController.settingsUpdateStream.stream + .listen((_) => matrixState.client.updateBotOptions()); + } + + Future _clearCache({List exclude = const []}) async { + final List> futures = []; + for (final key in _storageKeys) { + if (exclude.contains(key)) continue; + futures.add(GetStorage(key).erase()); + } + await Future.wait(futures); + } + + Future _initAnalyticsControllers() async { + putAnalytics.initialize(); + await getAnalytics.initialize(); + } + + Future resetAnalytics() async { + _disposeAnalyticsControllers(); + await _initAnalyticsControllers(); + } + + Future _onLanguageUpdate(LanguageUpdate update) async { + final exclude = [ + 'analytics_storage', + 'course_location_media_storage', + 'course_location_storage', + 'course_media_storage', + ]; + + // only clear course data if the base language has changed + if (update.prevBaseLang == update.baseLang) { + exclude.addAll([ + 'course_storage', + 'course_topic_storage', + 'course_activity_storage', + ]); + } + + _clearCache(exclude: exclude); + matrixState.client.updateBotOptions(); + } + static final List _storageKeys = [ 'mode_list_storage', 'activity_plan_storage', @@ -96,186 +189,4 @@ class PangeaController { 'course_location_media_storage', 'language_mismatch', ]; - - Future clearCache({List exclude = const []}) async { - final List> futures = []; - for (final key in _storageKeys) { - if (exclude.contains(key)) continue; - futures.add(GetStorage(key).erase()); - } - await Future.wait(futures); - } - - Future checkHomeServerAction() async { - final client = await matrixState.getLoginClient(); - if (client.homeserver != null) { - await Future.delayed(Duration.zero); - return client; - } - - final String homeServer = - AppConfig.defaultHomeserver.trim().toLowerCase().replaceAll(' ', '-'); - var homeserver = Uri.parse(homeServer); - if (homeserver.scheme.isEmpty) { - homeserver = Uri.https(homeServer, ''); - } - - try { - await client.register(); - matrixState.loginRegistrationSupported = true; - } on MatrixException catch (e) { - matrixState.loginRegistrationSupported = - e.requireAdditionalAuthentication; - } - return client; - } - - /// check user information if not found then redirect to Date of birth page - void handleLoginStateChange( - LoginState state, - String? userID, - BuildContext context, - ) { - switch (state) { - case LoginState.loggedOut: - case LoginState.softLoggedOut: - // Reset cached analytics data - _disposeAnalyticsControllers(); - userController.clear(); - _languageSubscription?.cancel(); - _settingsSubscription?.cancel(); - _languageSubscription = null; - _settingsSubscription = null; - _logOutfromPangea(context); - break; - case LoginState.loggedIn: - // Initialize analytics data - initControllers(); - _setSettingsSubscriptions(); - - userController.reinitialize().then((_) { - final l1 = userController.profile.userSettings.sourceLanguage; - Provider.of(context, listen: false).setLocale(l1); - }); - subscriptionController.reinitialize(); - break; - } - - Sentry.configureScope( - (scope) => scope.setUser( - SentryUser( - id: userID, - name: userID, - ), - ), - ); - GoogleAnalytics.analyticsUserUpdate(userID); - } - - Future _initAnalyticsControllers() async { - putAnalytics.initialize(); - await getAnalytics.initialize(); - } - - void _disposeAnalyticsControllers() { - putAnalytics.dispose(); - getAnalytics.dispose(); - } - - Future resetAnalytics() async { - _disposeAnalyticsControllers(); - await _initAnalyticsControllers(); - } - - void _setSettingsSubscriptions() { - _languageSubscription?.cancel(); - _languageSubscription = - userController.languageStream.stream.listen(_onLanguageUpdate); - _settingsSubscription?.cancel(); - _settingsSubscription = userController.settingsUpdateStream.stream - .listen((_) => _updateBotOptions()); - } - - Future _onLanguageUpdate(LanguageUpdate update) async { - final exclude = [ - 'analytics_storage', - 'course_location_media_storage', - 'course_location_storage', - 'course_media_storage', - ]; - - // only clear course data if the base language has changed - if (update.prevBaseLang == update.baseLang) { - exclude.addAll([ - 'course_storage', - 'course_topic_storage', - 'course_activity_storage', - ]); - } - - clearCache(exclude: exclude); - _updateBotOptions(); - } - - Future _updateBotOptions() async { - if (!matrixState.client.isLogged()) return; - final botDM = matrixState.client.botDM; - if (botDM == null) { - return; - } - - final targetLanguage = userController.userL2?.langCode; - final cefrLevel = userController.profile.userSettings.cefrLevel; - final updateBotOptions = botDM.botOptions ?? BotOptionsModel(); - - if (updateBotOptions.targetLanguage == targetLanguage && - updateBotOptions.languageLevel == cefrLevel) { - return; - } - - updateBotOptions.targetLanguage = targetLanguage; - updateBotOptions.languageLevel = cefrLevel; - await botDM.setBotOptions(updateBotOptions); - } - - Future setPangeaPushRules() async { - if (!matrixState.client.isLogged()) return; - final List analyticsRooms = - matrixState.client.rooms.where((room) => room.isAnalyticsRoom).toList(); - - for (final Room room in analyticsRooms) { - final pushRule = room.pushRuleState; - if (pushRule != PushRuleState.dontNotify) { - await room.setPushRuleState(PushRuleState.dontNotify); - } - } - - if (!(matrixState.client.globalPushRules?.override?.any( - (element) => element.ruleId == PangeaEventTypes.textToSpeechRule, - ) ?? - false)) { - await matrixState.client.setPushRule( - PushRuleKind.override, - PangeaEventTypes.textToSpeechRule, - [PushRuleAction.dontNotify], - conditions: [ - PushCondition( - kind: 'event_match', - key: 'content.msgtype', - pattern: MessageTypes.Audio, - ), - PushCondition( - kind: 'event_match', - key: 'content.transcription.lang_code', - pattern: '*', - ), - PushCondition( - kind: 'event_match', - key: 'content.transcription.text', - pattern: '*', - ), - ], - ); - } - } } diff --git a/lib/pangea/learning_settings/settings_learning.dart b/lib/pangea/learning_settings/settings_learning.dart index 5c5369003..6297bbab4 100644 --- a/lib/pangea/learning_settings/settings_learning.dart +++ b/lib/pangea/learning_settings/settings_learning.dart @@ -19,7 +19,7 @@ import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dar import 'package:fluffychat/pangea/learning_settings/settings_learning_view.dart'; import 'package:fluffychat/pangea/learning_settings/tool_settings_enum.dart'; import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; -import 'package:fluffychat/pangea/user/models/user_model.dart'; +import 'package:fluffychat/pangea/user/user_model.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/matrix.dart'; diff --git a/lib/pangea/space_analytics/space_analytics.dart b/lib/pangea/space_analytics/space_analytics.dart index 1f007d196..d131d070c 100644 --- a/lib/pangea/space_analytics/space_analytics.dart +++ b/lib/pangea/space_analytics/space_analytics.dart @@ -19,7 +19,7 @@ import 'package:fluffychat/pangea/space_analytics/space_analytics_download_enum. import 'package:fluffychat/pangea/space_analytics/space_analytics_inactive_dialog.dart'; import 'package:fluffychat/pangea/space_analytics/space_analytics_request_dialog.dart'; import 'package:fluffychat/pangea/space_analytics/space_analytics_view.dart'; -import 'package:fluffychat/pangea/user/models/analytics_profile_model.dart'; +import 'package:fluffychat/pangea/user/analytics_profile_model.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/matrix.dart'; diff --git a/lib/pangea/spaces/widgets/load_participants_builder.dart b/lib/pangea/spaces/widgets/load_participants_builder.dart index 709f1557b..1ba9ee35a 100644 --- a/lib/pangea/spaces/widgets/load_participants_builder.dart +++ b/lib/pangea/spaces/widgets/load_participants_builder.dart @@ -5,7 +5,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; -import 'package:fluffychat/pangea/user/models/analytics_profile_model.dart'; +import 'package:fluffychat/pangea/user/analytics_profile_model.dart'; import 'package:fluffychat/widgets/matrix.dart'; class LoadParticipantsBuilder extends StatefulWidget { diff --git a/lib/pangea/subscription/controllers/subscription_controller.dart b/lib/pangea/subscription/controllers/subscription_controller.dart index 832e49422..69d9c5276 100644 --- a/lib/pangea/subscription/controllers/subscription_controller.dart +++ b/lib/pangea/subscription/controllers/subscription_controller.dart @@ -24,7 +24,7 @@ import 'package:fluffychat/pangea/subscription/repo/subscription_management_repo import 'package:fluffychat/pangea/subscription/repo/subscription_repo.dart'; import 'package:fluffychat/pangea/subscription/utils/subscription_app_id.dart'; import 'package:fluffychat/pangea/subscription/widgets/subscription_paywall.dart'; -import 'package:fluffychat/pangea/user/controllers/user_controller.dart'; +import 'package:fluffychat/pangea/user/user_controller.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/matrix.dart'; diff --git a/lib/pangea/subscription/widgets/subscription_paywall.dart b/lib/pangea/subscription/widgets/subscription_paywall.dart index b76712f3d..ee4e0b344 100644 --- a/lib/pangea/subscription/widgets/subscription_paywall.dart +++ b/lib/pangea/subscription/widgets/subscription_paywall.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/subscription/widgets/subscription_options.dart'; +import 'package:fluffychat/widgets/matrix.dart'; class SubscriptionPaywall extends StatelessWidget { final PangeaController pangeaController; @@ -30,7 +31,7 @@ class SubscriptionPaywall extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - if (pangeaController.matrixState.client.rooms.length > 1) ...[ + if (Matrix.of(context).client.rooms.length > 1) ...[ Text( L10n.of(context).welcomeBack, textAlign: TextAlign.center, diff --git a/lib/pangea/toolbar/widgets/message_selection_positioner.dart b/lib/pangea/toolbar/widgets/message_selection_positioner.dart index c1bf6cb26..88ce53576 100644 --- a/lib/pangea/toolbar/widgets/message_selection_positioner.dart +++ b/lib/pangea/toolbar/widgets/message_selection_positioner.dart @@ -109,7 +109,7 @@ class MessageSelectionPositionerState extends State _reactionSubscription?.cancel(); _contentChangedSubscription?.cancel(); scrollController?.dispose(); - MatrixState.pangeaController.matrixState.audioPlayer + Matrix.of(context).audioPlayer ?..stop() ..dispose(); super.dispose(); diff --git a/lib/pangea/user/models/activities_profile_model.dart b/lib/pangea/user/activities_profile_model.dart similarity index 100% rename from lib/pangea/user/models/activities_profile_model.dart rename to lib/pangea/user/activities_profile_model.dart diff --git a/lib/pangea/user/models/analytics_profile_model.dart b/lib/pangea/user/analytics_profile_model.dart similarity index 100% rename from lib/pangea/user/models/analytics_profile_model.dart rename to lib/pangea/user/analytics_profile_model.dart diff --git a/lib/pangea/user/utils/p_login.dart b/lib/pangea/user/p_login.dart similarity index 100% rename from lib/pangea/user/utils/p_login.dart rename to lib/pangea/user/p_login.dart diff --git a/lib/pangea/user/utils/p_logout.dart b/lib/pangea/user/p_logout.dart similarity index 100% rename from lib/pangea/user/utils/p_logout.dart rename to lib/pangea/user/p_logout.dart diff --git a/lib/pangea/user/pangea_push_rules_extension.dart b/lib/pangea/user/pangea_push_rules_extension.dart new file mode 100644 index 000000000..ca670f69b --- /dev/null +++ b/lib/pangea/user/pangea_push_rules_extension.dart @@ -0,0 +1,47 @@ +import 'package:matrix/matrix.dart'; + +import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; +import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; + +extension PangeaPushRulesExtension on Client { + Future setPangeaPushRules() async { + if (!isLogged()) return; + final List analyticsRooms = + rooms.where((room) => room.isAnalyticsRoom).toList(); + + for (final Room room in analyticsRooms) { + final pushRule = room.pushRuleState; + if (pushRule != PushRuleState.dontNotify) { + await room.setPushRuleState(PushRuleState.dontNotify); + } + } + + if (!(globalPushRules?.override?.any( + (element) => element.ruleId == PangeaEventTypes.textToSpeechRule, + ) ?? + false)) { + await setPushRule( + PushRuleKind.override, + PangeaEventTypes.textToSpeechRule, + [PushRuleAction.dontNotify], + conditions: [ + PushCondition( + kind: 'event_match', + key: 'content.msgtype', + pattern: MessageTypes.Audio, + ), + PushCondition( + kind: 'event_match', + key: 'content.transcription.lang_code', + pattern: '*', + ), + PushCondition( + kind: 'event_match', + key: 'content.transcription.text', + pattern: '*', + ), + ], + ); + } + } +} diff --git a/lib/pangea/user/controllers/user_controller.dart b/lib/pangea/user/user_controller.dart similarity index 97% rename from lib/pangea/user/controllers/user_controller.dart rename to lib/pangea/user/user_controller.dart index 7143226f0..bc93190e5 100644 --- a/lib/pangea/user/controllers/user_controller.dart +++ b/lib/pangea/user/user_controller.dart @@ -17,9 +17,9 @@ import 'package:fluffychat/pangea/languages/language_model.dart'; import 'package:fluffychat/pangea/languages/language_service.dart'; import 'package:fluffychat/pangea/languages/p_language_store.dart'; import 'package:fluffychat/pangea/learning_settings/tool_settings_enum.dart'; -import 'package:fluffychat/pangea/user/models/activities_profile_model.dart'; -import 'package:fluffychat/pangea/user/models/analytics_profile_model.dart'; -import '../models/user_model.dart'; +import 'package:fluffychat/pangea/user/activities_profile_model.dart'; +import 'package:fluffychat/pangea/user/analytics_profile_model.dart'; +import 'user_model.dart'; class LanguageUpdate { final LanguageModel? prevBaseLang; @@ -370,8 +370,7 @@ class UserController { baseLanguage ??= _pangeaController.userController.userL1; if (targetLanguage == null || analyticsProfile == null) return; - final analyticsRoom = - _pangeaController.matrixState.client.analyticsRoomLocal(targetLanguage); + final analyticsRoom = client.analyticsRoomLocal(targetLanguage); if (analyticsProfile!.targetLanguage == targetLanguage && analyticsProfile!.baseLanguage == baseLanguage && @@ -396,8 +395,7 @@ class UserController { Future _addAnalyticsRoomIdsToPublicProfile() async { if (analyticsProfile?.languageAnalytics == null) return; - final analyticsRooms = - _pangeaController.matrixState.client.allMyAnalyticsRooms; + final analyticsRooms = client.allMyAnalyticsRooms; if (analyticsRooms.isEmpty) return; for (final analyticsRoom in analyticsRooms) { @@ -434,9 +432,7 @@ class UserController { analyticsProfile!.addXPOffset( targetLanguage, offset, - _pangeaController.matrixState.client - .analyticsRoomLocal(targetLanguage) - ?.id, + client.analyticsRoomLocal(targetLanguage)?.id, ); await _savePublicProfileUpdate( PangeaEventTypes.profileAnalytics, diff --git a/lib/pangea/user/models/user_model.dart b/lib/pangea/user/user_model.dart similarity index 99% rename from lib/pangea/user/models/user_model.dart rename to lib/pangea/user/user_model.dart index ee2f9557c..fae12f67b 100644 --- a/lib/pangea/user/models/user_model.dart +++ b/lib/pangea/user/user_model.dart @@ -6,7 +6,7 @@ import 'package:fluffychat/pangea/instructions/instruction_settings.dart'; import 'package:fluffychat/pangea/learning_settings/language_level_type_enum.dart'; import 'package:fluffychat/pangea/learning_settings/tool_settings_enum.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import '../../languages/language_model.dart'; +import '../languages/language_model.dart'; /// The user's settings learning settings. class UserSettings { diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index 408747c54..7411a8694 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -273,7 +273,7 @@ class MatrixState extends State with WidgetsBindingObserver { ), ), ); - pangeaController = PangeaController(matrix: widget, matrixState: this); + pangeaController = PangeaController(matrixState: this); WidgetsBinding.instance.addPostFrameCallback((_) { _setAppLanguage(); _setLanguageListener(); From 5e9840dbe6d03debab3634f467b155ad37ca82a1 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 4 Dec 2025 11:01:41 -0500 Subject: [PATCH 9/9] remove unreferenced functions from user controller --- lib/pages/chat_list/chat_list.dart | 1 - .../choreographer/igc/igc_controller.dart | 2 +- .../common/controllers/pangea_controller.dart | 2 +- lib/pangea/user/activities_profile_model.dart | 53 ----- lib/pangea/user/user_controller.dart | 181 +++--------------- 5 files changed, 28 insertions(+), 211 deletions(-) delete mode 100644 lib/pangea/user/activities_profile_model.dart diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index 6d1228d06..643151963 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -42,7 +42,6 @@ import '../../widgets/matrix.dart'; import 'package:fluffychat/utils/tor_stub.dart' if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart'; - enum PopupMenuAction { settings, invite, diff --git a/lib/pangea/choreographer/igc/igc_controller.dart b/lib/pangea/choreographer/igc/igc_controller.dart index 68db0d782..afe661ff9 100644 --- a/lib/pangea/choreographer/igc/igc_controller.dart +++ b/lib/pangea/choreographer/igc/igc_controller.dart @@ -68,7 +68,7 @@ class IgcController { ) => IGCRequestModel( fullText: text, - userId: MatrixState.pangeaController.userController.userId!, + userId: MatrixState.pangeaController.userController.client.userID!, userL1: MatrixState.pangeaController.userController.userL1Code!, userL2: MatrixState.pangeaController.userController.userL2Code!, enableIGC: true, diff --git a/lib/pangea/common/controllers/pangea_controller.dart b/lib/pangea/common/controllers/pangea_controller.dart index 200eaff66..89a268a7c 100644 --- a/lib/pangea/common/controllers/pangea_controller.dart +++ b/lib/pangea/common/controllers/pangea_controller.dart @@ -37,7 +37,7 @@ class PangeaController { final MatrixState matrixState; PangeaController({required this.matrixState}) { - userController = UserController(this); + userController = UserController(); getAnalytics = GetAnalyticsController(this); putAnalytics = PutAnalyticsController(this); subscriptionController = SubscriptionController(this); diff --git a/lib/pangea/user/activities_profile_model.dart b/lib/pangea/user/activities_profile_model.dart deleted file mode 100644 index b9e9d41ed..000000000 --- a/lib/pangea/user/activities_profile_model.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:fluffychat/pangea/common/constants/model_keys.dart'; -import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; - -class ActivitiesProfileModel { - final List _bookmarkedActivities; - - ActivitiesProfileModel({ - required List bookmarkedActivities, - }) : _bookmarkedActivities = bookmarkedActivities; - - static ActivitiesProfileModel get empty => ActivitiesProfileModel( - bookmarkedActivities: [], - ); - - bool isBookmarked(String id) => _bookmarkedActivities.contains(id); - - void addBookmark(String activityId) { - if (!_bookmarkedActivities.contains(activityId)) { - _bookmarkedActivities.add(activityId); - } - } - - void removeBookmark(String activityId) { - _bookmarkedActivities.remove(activityId); - } - - // Future> getBookmarkedActivities() => Future.wait( - // _bookmarkedActivities.map((id) => ActivityPlanRepo.get(id)).toList(), - // ); - - // List getBookmarkedActivitiesSync() => _bookmarkedActivities - // .map((id) => ActivityPlanRepo.getCached(id)) - // .whereType() - // .toList(); - - static ActivitiesProfileModel fromJson(Map json) { - if (!json.containsKey(PangeaEventTypes.profileActivities)) { - return ActivitiesProfileModel.empty; - } - - final profileJson = json[PangeaEventTypes.profileActivities]; - return ActivitiesProfileModel( - bookmarkedActivities: - List.from(profileJson[ModelKey.bookmarkedActivities] ?? []), - ); - } - - Map toJson() { - return { - ModelKey.bookmarkedActivities: _bookmarkedActivities, - }; - } -} diff --git a/lib/pangea/user/user_controller.dart b/lib/pangea/user/user_controller.dart index bc93190e5..a7ebbbbc6 100644 --- a/lib/pangea/user/user_controller.dart +++ b/lib/pangea/user/user_controller.dart @@ -1,14 +1,12 @@ import 'dart:async'; import 'package:collection/collection.dart'; -import 'package:jwt_decode/jwt_decode.dart'; import 'package:matrix/matrix.dart' as matrix; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart'; import 'package:fluffychat/pangea/bot/utils/bot_name.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; -import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; @@ -17,8 +15,8 @@ import 'package:fluffychat/pangea/languages/language_model.dart'; import 'package:fluffychat/pangea/languages/language_service.dart'; import 'package:fluffychat/pangea/languages/p_language_store.dart'; import 'package:fluffychat/pangea/learning_settings/tool_settings_enum.dart'; -import 'package:fluffychat/pangea/user/activities_profile_model.dart'; import 'package:fluffychat/pangea/user/analytics_profile_model.dart'; +import 'package:fluffychat/widgets/matrix.dart'; import 'user_model.dart'; class LanguageUpdate { @@ -37,43 +35,28 @@ class LanguageUpdate { /// Controller that manages saving and reading of user/profile information class UserController { - late PangeaController _pangeaController; - final StreamController languageStream = StreamController.broadcast(); final StreamController settingsUpdateStream = StreamController.broadcast(); - UserController(PangeaController pangeaController) : super() { - _pangeaController = pangeaController; - } - - matrix.Client get client => _pangeaController.matrixState.client; - - /// Convenience function that returns the user ID currently stored in the client. - String? get userId => client.userID; - /// Cached version of the user profile, so it doesn't have /// to be read in from client's account data each time it is accessed. Profile? _cachedProfile; AnalyticsProfileModel? analyticsProfile; - ActivitiesProfileModel? activitiesProfile; /// Listens for account updates and updates the cached profile StreamSubscription? _profileListener; - /// Listen for updates to account data in syncs and update the cached profile - void _addProfileListener() { - _profileListener ??= client.onSync.stream - .where((sync) => sync.accountData != null) - .listen((sync) { - final profileData = client.accountData[ModelKey.userProfile]?.content; - final Profile? fromAccountData = Profile.fromAccountData(profileData); - if (fromAccountData != null) { - _cachedProfile = fromAccountData; - } - }); + matrix.Client get client => MatrixState.pangeaController.matrixState.client; + + void _onProfileUpdate(matrix.SyncUpdate sync) { + final profileData = client.accountData[ModelKey.userProfile]?.content; + final Profile? fromAccountData = Profile.fromAccountData(profileData); + if (fromAccountData != null) { + _cachedProfile = fromAccountData; + } } /// The user's profile. Will be empty if the client's accountData hasn't @@ -109,8 +92,8 @@ class UserController { waitForDataInSync = false, }) async { await initialize(); - final prevTargetLang = _pangeaController.userController.userL2; - final prevBaseLang = _pangeaController.userController.userL1; + final prevTargetLang = userL2; + final prevBaseLang = userL1; final prevHash = profile.hashCode; final Profile updatedProfile = update(profile); @@ -121,12 +104,11 @@ class UserController { await updatedProfile.saveProfileData(waitForDataInSync: waitForDataInSync); - if ((prevTargetLang != _pangeaController.userController.userL2) || - (prevBaseLang != _pangeaController.userController.userL1)) { + if ((prevTargetLang != userL2) || (prevBaseLang != userL1)) { languageStream.add( LanguageUpdate( - baseLang: _pangeaController.userController.userL1!, - targetLang: _pangeaController.userController.userL2!, + baseLang: userL1!, + targetLang: userL2!, prevBaseLang: prevBaseLang, prevTargetLang: prevTargetLang, ), @@ -152,12 +134,16 @@ class UserController { try { await _initialize(); - _addProfileListener(); + + _profileListener ??= client.onSync.stream + .where((sync) => sync.accountData != null) + .listen(_onProfileUpdate); + _addAnalyticsRoomIdsToPublicProfile(); if (profile.userSettings.targetLanguage != null && profile.userSettings.targetLanguage!.isNotEmpty && - _pangeaController.userController.userL2 == null) { + userL2 == null) { // update the language list and send an update to refresh analytics summary await PLanguageStore.initialize(forceRefresh: true); } @@ -191,24 +177,22 @@ class UserController { final resp = await client.getUserProfile(client.userID!); analyticsProfile = AnalyticsProfileModel.fromJson(resp.additionalProperties); - activitiesProfile = - ActivitiesProfileModel.fromJson(resp.additionalProperties); } catch (e) { // getting a 404 error for some users without pre-existing profile // still want to set other properties, so catch this error analyticsProfile = AnalyticsProfileModel(); - activitiesProfile = ActivitiesProfileModel.empty; } // Do not await. This function pulls level from analytics, // so it waits for analytics to finish initializing. Analytics waits for user controller to // finish initializing, so this would cause a deadlock. if (analyticsProfile!.isEmpty) { - _pangeaController.getAnalytics.initCompleter.future + MatrixState.pangeaController.getAnalytics.initCompleter.future .timeout(const Duration(seconds: 10)) .then((_) { updateAnalyticsProfile( - level: _pangeaController.getAnalytics.constructListModel.level, + level: MatrixState + .pangeaController.getAnalytics.constructListModel.level, ); }).catchError((e, s) { ErrorHandler.logError( @@ -240,9 +224,6 @@ class UserController { await initialize(); } - /// Returns a boolean value indicating whether a new JWT (JSON Web Token) is needed. - bool needNewJWT(String token) => Jwt.isExpired(token); - /// Retrieves matrix access token. String get accessToken { final token = client.accessToken; @@ -252,21 +233,6 @@ class UserController { return token; } - /// Returns the full name of the user. - /// If the [userId] is null, an error will be logged and null will be returned. - /// The full name is obtained by extracting the substring before the first occurrence of ":" in the [userId] - /// and then replacing all occurrences of "@" with an empty string. - String? get fullname { - if (userId == null) { - ErrorHandler.logError( - e: "calling fullname with userId == null", - data: {}, - ); - return null; - } - return userId!.substring(0, userId!.indexOf(":")).replaceAll("@", ""); - } - /// Checks if user data is available and the user's l2 is set. Future get isUserL2Set async { try { @@ -296,39 +262,6 @@ class UserController { ); } - /// Checks if the user's languages are set. - /// Returns a [Future] that completes with a [bool] value - /// indicating whether the user's languages are set. - /// - /// A user's languages are considered set if the source and target languages - /// are not null, not empty, and not equal to the [LanguageKeys.unknownLanguage] constant. - /// - /// If an error occurs during the process, it logs the error and returns `false`. - Future get areUserLanguagesSet async { - try { - final String? srcLang = profile.userSettings.sourceLanguage; - final String? tgtLang = profile.userSettings.targetLanguage; - return srcLang != null && - tgtLang != null && - srcLang.isNotEmpty && - tgtLang.isNotEmpty && - srcLang != LanguageKeys.unknownLanguage && - tgtLang != LanguageKeys.unknownLanguage; - } catch (err, s) { - ErrorHandler.logError( - e: err, - s: s, - data: {}, - ); - return false; - } - } - - /// Returns a boolean value indicating whether the user's profile is public. - bool get isPublic { - return profile.userSettings.publicProfile ?? true; - } - /// Retrieves the user's email address. /// /// This method fetches the user's email address by making a request to the @@ -366,8 +299,8 @@ class UserController { LanguageModel? baseLanguage, LanguageModel? targetLanguage, }) async { - targetLanguage ??= _pangeaController.userController.userL2; - baseLanguage ??= _pangeaController.userController.userL1; + targetLanguage ??= userL2; + baseLanguage ??= userL1; if (targetLanguage == null || analyticsProfile == null) return; final analyticsRoom = client.analyticsRoomLocal(targetLanguage); @@ -426,7 +359,7 @@ class UserController { } Future addXPOffset(int offset) async { - final targetLanguage = _pangeaController.userController.userL2; + final targetLanguage = userL2; if (targetLanguage == null || analyticsProfile == null) return; analyticsProfile!.addXPOffset( @@ -440,68 +373,6 @@ class UserController { ); } - Future addBookmarkedActivity({ - required String activityId, - }) async { - if (activitiesProfile == null) { - throw Exception("Activities profile is not initialized"); - } - - activitiesProfile!.addBookmark(activityId); - await _savePublicProfileUpdate( - PangeaEventTypes.profileActivities, - activitiesProfile!.toJson(), - ); - } - - // Future> getBookmarkedActivities() async { - // if (activitiesProfile == null) { - // throw Exception("Activities profile is not initialized"); - // } - - // return activitiesProfile!.getBookmarkedActivities(); - // } - - // List getBookmarkedActivitiesSync() { - // if (activitiesProfile == null) { - // throw Exception("Activities profile is not initialized"); - // } - - // return activitiesProfile!.getBookmarkedActivitiesSync(); - // } - - Future updateBookmarkedActivity({ - required String activityId, - required String newActivityId, - }) async { - if (activitiesProfile == null) { - throw Exception("Activities profile is not initialized"); - } - - activitiesProfile!.removeBookmark(activityId); - activitiesProfile!.addBookmark(newActivityId); - await _savePublicProfileUpdate( - PangeaEventTypes.profileActivities, - activitiesProfile!.toJson(), - ); - } - - Future removeBookmarkedActivity({ - required String activityId, - }) async { - if (activitiesProfile == null) { - throw Exception("Activities profile is not initialized"); - } - - activitiesProfile!.removeBookmark(activityId); - await _savePublicProfileUpdate( - PangeaEventTypes.profileActivities, - activitiesProfile!.toJson(), - ); - } - - bool isBookmarked(String id) => activitiesProfile?.isBookmarked(id) ?? false; - Future getPublicAnalyticsProfile( String userId, ) async {