From b1c26f057286e3071ac67842bcde801b0cccf116 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Thu, 11 Apr 2024 16:31:10 -0400 Subject: [PATCH 1/7] speech to text fully drafted --- lib/pages/chat/events/message.dart | 3 +- lib/pages/chat/events/message_content.dart | 3 +- lib/pangea/controllers/pangea_controller.dart | 3 + .../speech_to_text_controller.dart | 81 ++ .../text_to_speech_controller.dart | 19 - lib/pangea/enum/audio_encoding_enum.dart | 56 + lib/pangea/enum/message_mode_enum.dart | 48 + lib/pangea/models/pangea_message_event.dart | 12 +- lib/pangea/models/speech_to_text_models.dart | 137 +++ lib/pangea/network/urls.dart | 1 + lib/pangea/repo/text_to_speech_repo.dart | 66 -- .../widgets/chat/message_audio_card.dart | 2 +- .../chat/message_speech_to_text_card.dart | 187 ++++ lib/pangea/widgets/chat/message_toolbar.dart | 98 +- .../chat/message_unsubscribed_card.dart | 3 +- .../widgets/chat/text_to_speech_button.dart | 4 +- lib/pangea/widgets/igc/pangea_rich_text.dart | 3 +- needed-translations.txt | 958 ++++++++++++++++-- 18 files changed, 1467 insertions(+), 217 deletions(-) create mode 100644 lib/pangea/controllers/speech_to_text_controller.dart create mode 100644 lib/pangea/enum/audio_encoding_enum.dart create mode 100644 lib/pangea/enum/message_mode_enum.dart create mode 100644 lib/pangea/models/speech_to_text_models.dart delete mode 100644 lib/pangea/repo/text_to_speech_repo.dart create mode 100644 lib/pangea/widgets/chat/message_speech_to_text_card.dart diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 5b1a56cf5..7ce2c9cc3 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -145,7 +145,8 @@ class Message extends StatelessWidget { controller.getPangeaMessageEvent(event.eventId); ToolbarDisplayController? toolbarController; if (event.messageType == MessageTypes.Text || - event.messageType == MessageTypes.Notice) { + event.messageType == MessageTypes.Notice || + event.messageType == MessageTypes.Audio) { toolbarController = controller.getToolbarDisplayController(event.eventId); } // Pangea# diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 9b930cf83..301e972f4 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -1,5 +1,6 @@ import 'package:fluffychat/pages/chat/events/html_message.dart'; import 'package:fluffychat/pages/chat/events/video_player.dart'; +import 'package:fluffychat/pangea/enum/message_mode_enum.dart'; import 'package:fluffychat/pangea/models/pangea_message_event.dart'; import 'package:fluffychat/pangea/widgets/chat/message_context_menu.dart'; import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; @@ -340,7 +341,7 @@ class MessageContent extends StatelessWidget { ), onListen: () => toolbarController?.showToolbar( context, - mode: MessageMode.play, + mode: MessageMode.conversion, ), ), enableInteractiveSelection: diff --git a/lib/pangea/controllers/pangea_controller.dart b/lib/pangea/controllers/pangea_controller.dart index 86946f314..ddc619d27 100644 --- a/lib/pangea/controllers/pangea_controller.dart +++ b/lib/pangea/controllers/pangea_controller.dart @@ -10,6 +10,7 @@ import 'package:fluffychat/pangea/controllers/local_settings.dart'; import 'package:fluffychat/pangea/controllers/message_data_controller.dart'; import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart'; import 'package:fluffychat/pangea/controllers/permissions_controller.dart'; +import 'package:fluffychat/pangea/controllers/speech_to_text_controller.dart'; import 'package:fluffychat/pangea/controllers/subscription_controller.dart'; import 'package:fluffychat/pangea/controllers/text_to_speech_controller.dart'; import 'package:fluffychat/pangea/controllers/user_controller.dart'; @@ -47,6 +48,7 @@ class PangeaController { late InstructionsController instructions; late SubscriptionController subscriptionController; late TextToSpeechController textToSpeech; + late SpeechToTextController speechToText; ///store Services late PLocalStore pStoreService; @@ -93,6 +95,7 @@ class PangeaController { subscriptionController = SubscriptionController(this); itFeedback = ITFeedbackController(this); textToSpeech = TextToSpeechController(this); + speechToText = SpeechToTextController(this); PAuthGaurd.pController = this; } diff --git a/lib/pangea/controllers/speech_to_text_controller.dart b/lib/pangea/controllers/speech_to_text_controller.dart new file mode 100644 index 000000000..c0eefb390 --- /dev/null +++ b/lib/pangea/controllers/speech_to_text_controller.dart @@ -0,0 +1,81 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; +import 'package:fluffychat/pangea/models/speech_to_text_models.dart'; +import 'package:flutter/foundation.dart'; +import 'package:http/http.dart'; + +import '../config/environment.dart'; +import '../network/requests.dart'; +import '../network/urls.dart'; + +// Assuming SpeechToTextRequestModel, SpeechToTextResponseModel 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: 15); + _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: await _pangeaController.userController.accessToken, + requestModel: requestModel, + ); + _cache[cacheKey] = _SpeechToTextCacheItem(data: response); + return response; + } + } + + static 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)); + return SpeechToTextResponseModel.fromJson(json); + } else { + debugPrint('Error converting speech to text: ${res.body}'); + throw Exception('Failed to convert speech to text'); + } + } +} diff --git a/lib/pangea/controllers/text_to_speech_controller.dart b/lib/pangea/controllers/text_to_speech_controller.dart index b7007ad1a..b34092618 100644 --- a/lib/pangea/controllers/text_to_speech_controller.dart +++ b/lib/pangea/controllers/text_to_speech_controller.dart @@ -126,25 +126,6 @@ class TextToSpeechController { return TextToSpeechResponse.fromJson(json); } - // if (json["wave_form"] == null) { - // json["wave_form"] = getWaveForm(); - // } - - // return TextToSpeechResponse( - // audioContent: String.fromCharCodes(base64Decode(json["audio_content"])), - // mediaType: json["media_type"], - // durationMillis: durationMillis(json["duration_millis"]), - // waveform: getWaveForm(json["audio_content"]), - // ); - // } - - // static List getWaveForm(audioContent) { - // return []; - // } - - // static int durationMillis(audioContent) { - // return 0; - // } static bool isOggFile(Uint8List bytes) { // Check if the file has enough bytes for the header diff --git a/lib/pangea/enum/audio_encoding_enum.dart b/lib/pangea/enum/audio_encoding_enum.dart new file mode 100644 index 000000000..729dec147 --- /dev/null +++ b/lib/pangea/enum/audio_encoding_enum.dart @@ -0,0 +1,56 @@ +AudioEncodingEnum mimeTypeToAudioEncoding(String mimeType) { + switch (mimeType) { + case 'audio/mpeg': + return AudioEncodingEnum.mp3; + case 'audio/mp4': + return AudioEncodingEnum.mp4; + case 'audio/ogg': + return AudioEncodingEnum.oggOpus; + default: + return AudioEncodingEnum.encodingUnspecified; + } +} + +enum AudioEncodingEnum { + encodingUnspecified, + linear16, + flac, + mulaw, + amr, + amrWb, + oggOpus, + speexWithHeaderByte, + mp3, + mp4, + 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: + return 'LINEAR16'; + case AudioEncodingEnum.flac: + return 'FLAC'; + case AudioEncodingEnum.mulaw: + return 'MULAW'; + case AudioEncodingEnum.amr: + return 'AMR'; + case AudioEncodingEnum.amrWb: + return 'AMR_WB'; + case AudioEncodingEnum.oggOpus: + return 'OGG_OPUS'; + case AudioEncodingEnum.speexWithHeaderByte: + return 'SPEEX_WITH_HEADER_BYTE'; + case AudioEncodingEnum.mp3: + return 'MP3'; + case AudioEncodingEnum.mp4: + return 'MP4'; + case AudioEncodingEnum.webmOpus: + return 'WEBM_OPUS'; + default: + return 'ENCODING_UNSPECIFIED'; + } + } +} diff --git a/lib/pangea/enum/message_mode_enum.dart b/lib/pangea/enum/message_mode_enum.dart new file mode 100644 index 000000000..e4d5c5c5d --- /dev/null +++ b/lib/pangea/enum/message_mode_enum.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +enum MessageMode { conversion, translation, definition } + +extension MessageModeExtension on MessageMode { + IconData icon(bool isAudioMessage) { + switch (this) { + case MessageMode.translation: + return Icons.g_translate; + case MessageMode.conversion: + return Icons.play_arrow; + //TODO change icon for audio messages + case MessageMode.definition: + return Icons.book; + default: + return Icons.error; // Icon to indicate an error or unsupported mode + } + } + + String title(BuildContext context) { + switch (this) { + case MessageMode.translation: + return L10n.of(context)!.translations; + case MessageMode.conversion: + return L10n.of(context)!.messageAudio; + case MessageMode.definition: + return L10n.of(context)!.definitions; + default: + return L10n.of(context)! + .oopsSomethingWentWrong; // Title to indicate an error or unsupported mode + } + } + + String tooltip(BuildContext context) { + switch (this) { + case MessageMode.translation: + return L10n.of(context)!.translationTooltip; + case MessageMode.conversion: + return L10n.of(context)!.audioTooltip; + case MessageMode.definition: + return L10n.of(context)!.define; + default: + return L10n.of(context)! + .oopsSomethingWentWrong; // Title to indicate an error or unsupported mode + } + } +} diff --git a/lib/pangea/models/pangea_message_event.dart b/lib/pangea/models/pangea_message_event.dart index ebc8c2665..ccc44e49a 100644 --- a/lib/pangea/models/pangea_message_event.dart +++ b/lib/pangea/models/pangea_message_event.dart @@ -148,7 +148,7 @@ class PangeaMessageEvent { }, ); - debugPrint("eventId in getAudioGlobal $eventId"); + debugPrint("eventId in getTextToSpeechGlobal $eventId"); final Event? audioEvent = eventId != null ? await room.getEventById(eventId) : null; @@ -162,10 +162,10 @@ class PangeaMessageEvent { //get audio for text and language //if no audio exists, create it //if audio exists, return it - Future getAudioGlobal(String langCode) async { + Future getTextToSpeechGlobal(String langCode) async { final String text = representationByLanguage(langCode)?.text ?? body; - final local = getAudioLocal(langCode, text); + final local = getTextToSpeechLocal(langCode, text); if (local != null) return Future.value(local); @@ -223,16 +223,16 @@ class PangeaMessageEvent { // .timeout( // Durations.long4, // onTimeout: () { - // debugPrint("timeout in getAudioGlobal"); + // debugPrint("timeout in getTextToSpeechGlobal"); // return null; // }, // ); - debugPrint("eventId in getAudioGlobal $eventId"); + debugPrint("eventId in getTextToSpeechGlobal $eventId"); return eventId != null ? room.getEventById(eventId) : null; } - Event? getAudioLocal(String langCode, String text) { + Event? getTextToSpeechLocal(String langCode, String text) { return allAudio.firstWhereOrNull( (element) { // Safely access the transcription map diff --git a/lib/pangea/models/speech_to_text_models.dart b/lib/pangea/models/speech_to_text_models.dart new file mode 100644 index 000000000..d55de33d9 --- /dev/null +++ b/lib/pangea/models/speech_to_text_models.dart @@ -0,0 +1,137 @@ +import 'dart:convert'; + +import 'package:fluffychat/pangea/enum/audio_encoding_enum.dart'; +import 'package:flutter/foundation.dart'; + +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, + }; +} + +class SpeechToTextRequestModel { + final Uint8List audioContent; + final SpeechToTextAudioConfigModel config; + + SpeechToTextRequestModel({ + required this.audioContent, + required this.config, + }); + + 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 WordInfo { + final String word; + final Duration? startTime; + final Duration? endTime; + final double? confidence; + + WordInfo({ + required this.word, + this.startTime, + this.endTime, + this.confidence, + }); + + factory WordInfo.fromJson(Map json) => WordInfo( + word: json['word'], + startTime: json['start_time'] != null + ? Duration(milliseconds: json['start_time']) + : null, + endTime: json['end_time'] != null + ? Duration(milliseconds: json['end_time']) + : null, + confidence: (json['confidence'] as num?)?.toDouble(), + ); +} + +class Transcript { + final String transcript; + final double confidence; + final List words; + + Transcript({ + required this.transcript, + required this.confidence, + required this.words, + }); + + factory Transcript.fromJson(Map json) => Transcript( + transcript: json['transcript'], + confidence: json['confidence'].toDouble(), + words: + (json['words'] as List).map((e) => WordInfo.fromJson(e)).toList(), + ); +} + +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(), + ); +} + +class SpeechToTextResponseModel { + final List results; + + SpeechToTextResponseModel({ + required this.results, + }); + + factory SpeechToTextResponseModel.fromJson(Map json) => + SpeechToTextResponseModel( + results: (json['results'] as List) + .map((e) => SpeechToTextResult.fromJson(e)) + .toList(), + ); +} diff --git a/lib/pangea/network/urls.dart b/lib/pangea/network/urls.dart index d559065ee..16d8bcd23 100644 --- a/lib/pangea/network/urls.dart +++ b/lib/pangea/network/urls.dart @@ -51,6 +51,7 @@ class PApiUrls { static String subseqStep = "/it_step"; static String textToSpeech = "${Environment.choreoApi}/text_to_speech"; + static String speechToText = "${Environment.choreoApi}/speech_to_text"; ///-------------------------------- revenue cat -------------------------- static String rcApiV1 = "https://api.revenuecat.com/v1"; diff --git a/lib/pangea/repo/text_to_speech_repo.dart b/lib/pangea/repo/text_to_speech_repo.dart deleted file mode 100644 index aafd299bd..000000000 --- a/lib/pangea/repo/text_to_speech_repo.dart +++ /dev/null @@ -1,66 +0,0 @@ -// import 'dart:async'; -// import 'dart:convert'; - -// import 'package:fluffychat/pangea/config/environment.dart'; -// import 'package:fluffychat/pangea/constants/model_keys.dart'; -// import 'package:fluffychat/pangea/network/urls.dart'; -// import 'package:http/http.dart'; - -// import '../network/requests.dart'; - -// class TextToSpeechRequest { -// String text; -// String langCode; - -// TextToSpeechRequest({required this.text, required this.langCode}); - -// Map toJson() => { -// ModelKey.text: text, -// ModelKey.langCode: langCode, -// }; -// } - -// class TextToSpeechResponse { -// String audioContent; -// String mediaType; -// int durationMillis; -// List waveform; - -// TextToSpeechResponse({ -// required this.audioContent, -// required this.mediaType, -// required this.durationMillis, -// required this.waveform, -// }); - -// factory TextToSpeechResponse.fromJson( -// Map json, -// ) => -// TextToSpeechResponse( -// audioContent: json["audio_content"], -// mediaType: json["media_type"], -// durationMillis: json["duration_millis"], -// waveform: List.from(json["wave_form"]), -// ); -// } - -// class TextToSpeechService { -// static Future get({ -// required String accessToken, -// required 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); -// } -// } diff --git a/lib/pangea/widgets/chat/message_audio_card.dart b/lib/pangea/widgets/chat/message_audio_card.dart index ddd8caf61..71e3fa629 100644 --- a/lib/pangea/widgets/chat/message_audio_card.dart +++ b/lib/pangea/widgets/chat/message_audio_card.dart @@ -33,7 +33,7 @@ class MessageAudioCardState extends State { widget.messageEvent.representationByLanguage(langCode)?.text; if (text != null) { final Event? localEvent = - widget.messageEvent.getAudioLocal(langCode, text); + widget.messageEvent.getTextToSpeechLocal(langCode, text); if (localEvent != null) { localAudioEvent = localEvent; if (mounted) setState(() => _isLoading = false); diff --git a/lib/pangea/widgets/chat/message_speech_to_text_card.dart b/lib/pangea/widgets/chat/message_speech_to_text_card.dart new file mode 100644 index 000000000..88b77e6cf --- /dev/null +++ b/lib/pangea/widgets/chat/message_speech_to_text_card.dart @@ -0,0 +1,187 @@ +import 'dart:developer'; + +import 'package:fluffychat/pangea/enum/audio_encoding_enum.dart'; +import 'package:fluffychat/pangea/models/message_data_models.dart'; +import 'package:fluffychat/pangea/models/pangea_message_event.dart'; +import 'package:fluffychat/pangea/models/speech_to_text_models.dart'; +import 'package:fluffychat/pangea/utils/bot_style.dart'; +import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:fluffychat/utils/localized_exception_extension.dart'; +import 'package:fluffychat/widgets/matrix.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:matrix/matrix.dart'; + +class MessageSpeechToTextCard extends StatefulWidget { + final PangeaMessageEvent messageEvent; + + const MessageSpeechToTextCard({ + super.key, + required this.messageEvent, + }); + + @override + MessageSpeechToTextCardState createState() => MessageSpeechToTextCardState(); +} + +enum AudioFileStatus { notDownloaded, downloading, downloaded } + +class MessageSpeechToTextCardState extends State { + PangeaRepresentation? repEvent; + String? transcription; + bool _fetchingTranscription = true; + AudioFileStatus status = AudioFileStatus.notDownloaded; + MatrixFile? matrixFile; + // File? audioFile; + + String? get l1Code => + MatrixState.pangeaController.languageController.activeL1Code( + roomID: widget.messageEvent.room.id, + ); + String? get l2Code => + MatrixState.pangeaController.languageController.activeL2Code( + roomID: widget.messageEvent.room.id, + ); + + // get transcription from local events + Future getLocalTranscription() async { + return "This is a dummy transcription"; + } + + // This code is duplicated from audio_player.dart. Is there some way to reuse that code? + Future _downloadAction() async { + // #Pangea + // if (status != AudioFileStatus.notDownloaded) return; + if (status != AudioFileStatus.notDownloaded) { + return; + } + // Pangea# + setState(() => status = AudioFileStatus.downloading); + try { + // #Pangea + // final matrixFile = await widget.event.downloadAndDecryptAttachment(); + final matrixFile = + await widget.messageEvent.event.downloadAndDecryptAttachment(); + // Pangea# + // File? file; + + // TODO: Test on mobile and see if we need this case + // if (!kIsWeb) { + // final tempDir = await getTemporaryDirectory(); + // final fileName = Uri.encodeComponent( + // // #Pangea + // // widget.event.attachmentOrThumbnailMxcUrl()!.pathSegments.last, + // widget.messageEvent.event + // .attachmentOrThumbnailMxcUrl()! + // .pathSegments + // .last, + // // Pangea# + // ); + // file = File('${tempDir.path}/${fileName}_${matrixFile.name}'); + // await file.writeAsBytes(matrixFile.bytes); + // } + + // audioFile = file; + this.matrixFile = matrixFile; + status = AudioFileStatus.downloaded; + } catch (e, s) { + Logs().v('Could not download audio file', e, s); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(e.toLocalizedString(context)), + ), + ); + } + } + + AudioEncodingEnum? get encoding { + if (matrixFile == null) return null; + return mimeTypeToAudioEncoding(matrixFile!.mimeType); + } + + // call API to transcribe audio + Future transcribeAudio() async { + await _downloadAction(); + + final localmatrixFile = matrixFile; + final info = matrixFile?.info; + + if (matrixFile == null) { + debugger(when: kDebugMode); + ErrorHandler.logError( + e: 'Audio file or matrix file is null ${widget.messageEvent.eventId}', + s: StackTrace.current, + data: widget.messageEvent.event.content, + ); + return null; + } + + if (l1Code == null || l2Code == null) { + debugger(when: kDebugMode); + ErrorHandler.logError( + e: 'Language codes are null ${widget.messageEvent.eventId}', + s: StackTrace.current, + data: widget.messageEvent.event.content, + ); + return null; + } + + final SpeechToTextResponseModel response = + await MatrixState.pangeaController.speechToText.get( + SpeechToTextRequestModel( + audioContent: matrixFile!.bytes, + config: SpeechToTextAudioConfigModel( + encoding: encoding ?? AudioEncodingEnum.encodingUnspecified, + //this is the default in the RecordConfig in record package + sampleRateHertz: 44100, + userL1: l1Code!, + userL2: l2Code!, + ), + ), + ); + return response.results.first.transcripts.first.transcript; + } + + // look for transcription in message event + // if not found, call API to transcribe audio + Future loadTranscription() async { + // transcription ??= await getLocalTranscription(); + transcription ??= await transcribeAudio(); + setState(() => _fetchingTranscription = false); + } + + @override + void initState() { + super.initState(); + loadTranscription(); + } + + @override + Widget build(BuildContext context) { + // if (!_fetchingTranscription && repEvent == null && transcription == null) { + // return const CardErrorWidget(); + // } + + return Padding( + padding: const EdgeInsets.all(8), + child: _fetchingTranscription + ? SizedBox( + height: 14, + width: 14, + child: CircularProgressIndicator( + strokeWidth: 2.0, + color: Theme.of(context).colorScheme.primary, + ), + ) + : transcription != null + ? Text( + transcription!, + style: BotStyle.text(context), + ) + : Text( + repEvent!.text, + style: BotStyle.text(context), + ), + ); + } +} diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 2e9627c71..1a722dac1 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -1,25 +1,27 @@ import 'dart:async'; +import 'dart:developer'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pangea/constants/local.key.dart'; +import 'package:fluffychat/pangea/enum/message_mode_enum.dart'; import 'package:fluffychat/pangea/models/pangea_message_event.dart'; import 'package:fluffychat/pangea/utils/any_state_holder.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/overlay.dart'; import 'package:fluffychat/pangea/widgets/chat/message_audio_card.dart'; +import 'package:fluffychat/pangea/widgets/chat/message_speech_to_text_card.dart'; import 'package:fluffychat/pangea/widgets/chat/message_text_selection.dart'; import 'package:fluffychat/pangea/widgets/chat/message_translation_card.dart'; import 'package:fluffychat/pangea/widgets/chat/message_unsubscribed_card.dart'; import 'package:fluffychat/pangea/widgets/chat/overlay_message.dart'; import 'package:fluffychat/pangea/widgets/igc/word_data_card.dart'; import 'package:fluffychat/widgets/matrix.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; -enum MessageMode { translation, play, definition } - class ToolbarDisplayController { final PangeaMessageEvent pangeaMessageEvent; final String targetId; @@ -90,6 +92,7 @@ class ToolbarDisplayController { ], ); } catch (err) { + debugger(when: kDebugMode); ErrorHandler.logError(e: err, s: StackTrace.current); return; } @@ -147,53 +150,12 @@ class MessageToolbar extends StatefulWidget { } class MessageToolbarState extends State { - Widget? child; + Widget? toolbarContent; MessageMode? currentMode; bool updatingMode = false; late StreamSubscription selectionStream; late StreamSubscription toolbarModeStream; - IconData getIconData(MessageMode mode) { - switch (mode) { - case MessageMode.translation: - return Icons.g_translate; - case MessageMode.play: - return Icons.play_arrow; - case MessageMode.definition: - return Icons.book; - default: - return Icons.error; // Icon to indicate an error or unsupported mode - } - } - - String getModeTitle(MessageMode mode) { - switch (mode) { - case MessageMode.translation: - return L10n.of(context)!.translations; - case MessageMode.play: - return L10n.of(context)!.messageAudio; - case MessageMode.definition: - return L10n.of(context)!.definitions; - default: - return L10n.of(context)! - .oopsSomethingWentWrong; // Title to indicate an error or unsupported mode - } - } - - String getModeTooltip(MessageMode mode) { - switch (mode) { - case MessageMode.translation: - return L10n.of(context)!.translationTooltip; - case MessageMode.play: - return L10n.of(context)!.audioTooltip; - case MessageMode.definition: - return L10n.of(context)!.define; - default: - return L10n.of(context)! - .oopsSomethingWentWrong; // Title to indicate an error or unsupported mode - } - } - void updateMode(MessageMode newMode) { if (updatingMode) return; debugPrint("updating toolbar mode"); @@ -204,8 +166,8 @@ class MessageToolbarState extends State { updatingMode = true; }); if (!subscribed) { - child = MessageUnsubscribedCard( - languageTool: getModeTitle(newMode), + toolbarContent = MessageUnsubscribedCard( + languageTool: newMode.title(context), mode: newMode, toolbarModeStream: widget.toolbarModeStream, ); @@ -214,8 +176,8 @@ class MessageToolbarState extends State { case MessageMode.translation: showTranslation(); break; - case MessageMode.play: - playAudio(); + case MessageMode.conversion: + showConversion(); break; case MessageMode.definition: showDefinition(); @@ -231,28 +193,35 @@ class MessageToolbarState extends State { void showTranslation() { debugPrint("show translation"); - child = MessageTranslationCard( + toolbarContent = MessageTranslationCard( messageEvent: widget.pangeaMessageEvent, immersionMode: widget.immersionMode, selection: widget.textSelection, ); } - void playAudio() { - debugPrint("play audio"); - child = MessageAudioCard( - messageEvent: widget.pangeaMessageEvent, - ); + void showConversion() { + debugPrint("show conversion"); + if (isAudioMessage) { + debugPrint("is audio message"); + toolbarContent = MessageSpeechToTextCard( + messageEvent: widget.pangeaMessageEvent, + ); + } else { + toolbarContent = MessageAudioCard( + messageEvent: widget.pangeaMessageEvent, + ); + } } void showDefinition() { if (widget.textSelection.selectedText == null || widget.textSelection.selectedText!.isEmpty) { - child = const SelectToDefine(); + toolbarContent = const SelectToDefine(); return; } - child = WordDataCard( + toolbarContent = WordDataCard( word: widget.textSelection.selectedText!, wordLang: widget.pangeaMessageEvent.messageDisplayLangCode, fullText: widget.textSelection.messageText, @@ -262,6 +231,10 @@ class MessageToolbarState extends State { ); } + bool get isAudioMessage { + return widget.pangeaMessageEvent.event.messageType == MessageTypes.Audio; + } + void showImage() {} void spellCheck() {} @@ -286,7 +259,7 @@ class MessageToolbarState extends State { ) ?? true; autoplay - ? updateMode(MessageMode.play) + ? updateMode(MessageMode.conversion) : updateMode(MessageMode.translation); }); @@ -339,8 +312,8 @@ class MessageToolbarState extends State { duration: FluffyThemes.animationDuration, child: Column( children: [ - child ?? const SizedBox(), - SizedBox(height: child == null ? 0 : 20), + toolbarContent ?? const SizedBox(), + SizedBox(height: toolbarContent == null ? 0 : 20), ], ), ), @@ -349,10 +322,13 @@ class MessageToolbarState extends State { Row( mainAxisSize: MainAxisSize.min, children: MessageMode.values.map((mode) { + if (mode == MessageMode.definition && isAudioMessage) { + return const SizedBox.shrink(); + } return Tooltip( - message: getModeTooltip(mode), + message: mode.tooltip(context), child: IconButton( - icon: Icon(getIconData(mode)), + icon: Icon(mode.icon(isAudioMessage)), color: currentMode == mode ? Theme.of(context).colorScheme.primary : null, diff --git a/lib/pangea/widgets/chat/message_unsubscribed_card.dart b/lib/pangea/widgets/chat/message_unsubscribed_card.dart index 2d37328ec..c0574aa6f 100644 --- a/lib/pangea/widgets/chat/message_unsubscribed_card.dart +++ b/lib/pangea/widgets/chat/message_unsubscribed_card.dart @@ -2,11 +2,12 @@ import 'dart:async'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/utils/bot_style.dart'; -import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import '../../enum/message_mode_enum.dart'; + class MessageUnsubscribedCard extends StatelessWidget { final String languageTool; final MessageMode mode; diff --git a/lib/pangea/widgets/chat/text_to_speech_button.dart b/lib/pangea/widgets/chat/text_to_speech_button.dart index ec3dbd0db..5facb684b 100644 --- a/lib/pangea/widgets/chat/text_to_speech_button.dart +++ b/lib/pangea/widgets/chat/text_to_speech_button.dart @@ -50,7 +50,7 @@ class _TextToSpeechButtonState extends State { Event? get localAudioEvent => langCode != null && text != null && text!.isNotEmpty - ? _pangeaMessageEvent.getAudioLocal(langCode!, text!) + ? _pangeaMessageEvent.getTextToSpeechLocal(langCode!, text!) : null; String? get langCode => @@ -69,7 +69,7 @@ class _TextToSpeechButtonState extends State { if (langCode == null || langCode!.isEmpty) return; setState(() => _isLoading = true); - await _pangeaMessageEvent.getAudioGlobal(langCode!); + await _pangeaMessageEvent.getTextToSpeechGlobal(langCode!); setState(() => _isLoading = false); } catch (e) { setState(() => _isLoading = false); diff --git a/lib/pangea/widgets/igc/pangea_rich_text.dart b/lib/pangea/widgets/igc/pangea_rich_text.dart index fb0fcef1c..e083a67b6 100644 --- a/lib/pangea/widgets/igc/pangea_rich_text.dart +++ b/lib/pangea/widgets/igc/pangea_rich_text.dart @@ -14,6 +14,7 @@ import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import '../../enum/message_mode_enum.dart'; import '../../models/pangea_match_model.dart'; class PangeaRichText extends StatefulWidget { @@ -157,7 +158,7 @@ class PangeaRichTextState extends State { ), onListen: () => widget.toolbarController?.showToolbar( context, - mode: MessageMode.play, + mode: MessageMode.conversion, ), ), TextSpan( diff --git a/needed-translations.txt b/needed-translations.txt index ce3531669..b5d0fb74a 100644 --- a/needed-translations.txt +++ b/needed-translations.txt @@ -815,7 +815,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "bn": [ @@ -1649,7 +1668,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "bo": [ @@ -2483,7 +2521,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "ca": [ @@ -3317,7 +3374,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "cs": [ @@ -4151,7 +4227,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "de": [ @@ -4954,7 +5049,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "el": [ @@ -5788,7 +5902,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "eo": [ @@ -6622,20 +6755,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" - ], - - "es": [ - "errorGettingAudio", - "define", - "listen", - "showDefinition", - "acceptedKeyVerification", - "canceledKeyVerification", - "completedKeyVerification", - "isReadyForKeyVerification", - "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "et": [ @@ -7454,7 +7593,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "eu": [ @@ -8257,7 +8415,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "fa": [ @@ -9091,7 +9268,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "fi": [ @@ -9925,7 +10121,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "fr": [ @@ -10759,7 +10974,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "ga": [ @@ -11593,7 +11827,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "gl": [ @@ -12396,7 +12649,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "he": [ @@ -13230,7 +13502,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "hi": [ @@ -14064,7 +14355,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "hr": [ @@ -14885,7 +15195,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "hu": [ @@ -15719,7 +16048,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "id": [ @@ -16553,7 +16901,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "ie": [ @@ -17387,7 +17754,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "it": [ @@ -18206,7 +18592,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "ja": [ @@ -19040,7 +19445,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "ko": [ @@ -19874,7 +20298,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "lt": [ @@ -20708,7 +21151,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "lv": [ @@ -21542,7 +22004,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "nb": [ @@ -22376,7 +22857,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "nl": [ @@ -23210,7 +23710,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "pl": [ @@ -24044,7 +24563,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "pt": [ @@ -24878,7 +25416,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "pt_BR": [ @@ -25681,7 +26238,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "pt_PT": [ @@ -26515,7 +27091,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "ro": [ @@ -27349,7 +27944,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "ru": [ @@ -28156,7 +28770,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "sk": [ @@ -28990,7 +29623,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "sl": [ @@ -29824,7 +30476,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "sr": [ @@ -30658,7 +31329,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "sv": [ @@ -31492,7 +32182,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "ta": [ @@ -32326,7 +33035,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "th": [ @@ -33160,7 +33888,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "tr": [ @@ -33979,7 +34726,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "uk": [ @@ -34798,7 +35564,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "vi": [ @@ -35632,7 +36417,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "zh": [ @@ -36451,7 +37255,26 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ], "zh_Hant": [ @@ -37285,6 +38108,25 @@ "completedKeyVerification", "isReadyForKeyVerification", "requestedKeyVerification", - "startedKeyVerification" + "startedKeyVerification", + "subscriptionPopupTitle", + "subscriptionPopupDesc", + "seeOptions", + "continuedWithoutSubscription", + "trialPeriodExpired", + "selectToDefine", + "translations", + "messageAudio", + "definitions", + "subscribedToUnlockTools", + "more", + "translationTooltip", + "audioTooltip", + "certifyAge", + "kickBotWarning", + "joinToView", + "refresh", + "autoPlayTitle", + "autoPlayDesc" ] } From f3bb717245e4b741b28fdc7193ba4f0fba64af85 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Sat, 13 Apr 2024 12:11:24 -0400 Subject: [PATCH 2/7] finished saving text as transcript event, going to change to rep event though --- assets/l10n/intl_en.arb | 57 +- assets/l10n/intl_es.arb | 3 +- lib/pages/chat/events/message_content.dart | 2 +- lib/pangea/constants/pangea_event_types.dart | 4 + .../constants/pangea_message_types.dart | 3 - .../speech_to_text_controller.dart | 43 +- lib/pangea/enum/message_mode_enum.dart | 16 +- lib/pangea/models/pangea_message_event.dart | 81 +- lib/pangea/models/speech_to_text_models.dart | 30 +- lib/pangea/utils/report_message.dart | 12 +- .../chat/message_speech_to_text_card.dart | 159 +-- lib/pangea/widgets/chat/message_toolbar.dart | 55 +- lib/pangea/widgets/igc/pangea_rich_text.dart | 5 +- needed-translations.txt | 955 +++++++++--------- 14 files changed, 737 insertions(+), 688 deletions(-) delete mode 100644 lib/pangea/constants/pangea_message_types.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 0783205a6..28107dc47 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2415,9 +2415,6 @@ "seconds": {} } }, - "pleaseEnterANumber": "Please enter a number greater than 0", - "archiveRoomDescription": "The chat will be moved to the archive. Other users will be able to see that you have left the chat.", - "roomUpgradeDescription": "The chat will then be recreated with the new room version. All participants will be notified that they need to switch to the new chat. You can find out more about room versions at https://spec.matrix.org/latest/rooms/", "allCorrect": "That's how I would say it! Nice!", "newWayAllGood": "That's not how I would have said it but it looks good!", "othersAreBetter": "Hm, there might be a better way to say that.", @@ -2568,15 +2565,7 @@ "placeholders": {} }, "copyClassLink": "Copy invite link", - "@copyClassLink": { - "type": "text", - "placeholders": {} - }, "copyClassLinkDesc": "Clicking this link will take students to the app, direct them to make an account and they will automatically join this space.", - "@copyClassLink": { - "type": "text", - "placeholders": {} - }, "copyClassCode": "Copy invite code", "inviteStudentByUserName": "Invite student by username", "@inviteStudentByUserName": { @@ -2748,11 +2737,6 @@ "type": "text", "placeholders": {} }, - "errorPleaseRefresh": "We're looking into it! Please reload and try again.", - "@errorPleaseRefresh": { - "type": "text", - "placeholders": {} - }, "joinWithClassCode": "Join class or exchange", "@joinWithClassCode": { "type": "text", @@ -2973,26 +2957,6 @@ "type": "text", "placeholders": {} }, - "error502504Title": "Wow, there are a lot of students online!", - "@error502504Title": { - "type": "text", - "placeholders": {} - }, - "error502504Desc": "Translation and grammar tools may be slow or unavailable while the Pangea bots catch up.", - "@error502504Desc": { - "type": "text", - "placeholders": {} - }, - "error404Title": "Translation error!", - "@error404Title": { - "type": "text", - "placeholders": {} - }, - "error404Desc": "Pangea Bot isn't sure how to translate that...", - "@error404Desc": { - "type": "text", - "placeholders": {} - }, "errorDisableIT": "Translation assistance is turned off.", "errorDisableIGC": "Grammar assistance is turned off.", "errorDisableLanguageAssistance": "Translation assistance and grammar assistance are turned off.", @@ -3095,11 +3059,6 @@ "type": "text", "placeholders": {} }, - "classDescription": "Space Description", - "@classDescription": { - "type": "text", - "placeholders": {} - }, "inviteStudentByUserNameDesc": "If your student already has an account, you can search for them.", "@inviteStudentByUserNameDesc": { "type": "text", @@ -3116,7 +3075,6 @@ "clickMessageTitle": "Need help?", "clickMessageBody": "Click messages to access definitions, translations, and audio!", "understandingMessagesTitle": "Definitions and translations!", - "addToClass": "Add this chat to ", "understandingMessagesBody": "Click underlined words for definitions. Translate with message options (upper right).", "allDone": "All done!", "vocab": "Vocabulary", @@ -3649,7 +3607,6 @@ "user": {} } }, - "decline": "Decline", "declinedInvitation": "Declined invitation", "acceptedInvitation": "Accepted invitation", "youreInvited": "📩 You're invited!", @@ -3728,7 +3685,6 @@ }, "acceptSelection": "Accept Correction", "acceptSelectionAnyway": "Use this anyway", - "replace": "Make correction", "makingActivity": "Making activity", "why": "Why?", "definition": "Definition", @@ -3751,12 +3707,6 @@ } }, "noTeachersFound": "No teachers found to report to", - "pushNotificationsNotAvailable": "Push notifications not available", - "learnMore": "Learn more", - "banUserDescription": "The user will be banned from the chat and will not be able to enter the chat again until they are unbanned.", - "unbanUserDescription": "The user will be able to enter the chat again if they try.", - "kickUserDescription": "The user is kicked out of the chat but not banned. In public chats, the user can rejoin at any time.", - "makeAdminDescription": "Once you make this user admin, you may not be able to undo this as they will then have the same permissions as you.", "pleaseEnterANumber": "Please enter a number greater than 0", "archiveRoomDescription": "The chat will be moved to the archive. Other users will be able to see that you have left the chat.", "roomUpgradeDescription": "The chat will then be recreated with the new room version. All participants will be notified that they need to switch to the new chat. You can find out more about room versions at https://spec.matrix.org/latest/rooms/", @@ -3776,10 +3726,6 @@ } }, "searchChatsRooms": "Search for #chats, @users...", - "groupName": "Group name", - "createGroupAndInviteUsers": "Create a group and invite users", - "groupCanBeFoundViaSearch": "Group can be found via search", - "inNoSpaces": "You are not a member of any classes or exchanges", "createClass": "Create class", "createExchange": "Create exchange", "viewArchive": "View Archive", @@ -3886,7 +3832,7 @@ "enableModerationDesc": "Enable automatic moderation to review messages before they are sent", "conversationLanguageLevel": "What is the language level of this conversation?", "showDefinition": "Show Definition", - "acceptedKeyVerification": "{sender} accepted key verification", + "acceptedKeyVerification": "{sender} accepted key verification", "@acceptedKeyVerification": { "type": "text", "placeholders": { @@ -3941,6 +3887,7 @@ "more": "More", "translationTooltip": "Translate", "audioTooltip": "Play Audio", + "transcriptTooltip": "Transcript", "certifyAge": "I certify that I am over {age} years of age", "@certifyAge": { "type": "text", diff --git a/assets/l10n/intl_es.arb b/assets/l10n/intl_es.arb index 4a2825a2d..723a3d7a2 100644 --- a/assets/l10n/intl_es.arb +++ b/assets/l10n/intl_es.arb @@ -4572,6 +4572,7 @@ "more": "Más", "translationTooltip": "Traducir", "audioTooltip": "Reproducir audio", + "transcriptTooltip": "Transcripción", "yourBirthdayPleaseShort": "Seleccione su grupo de edad", "certifyAge": "Certifico que soy mayor de {age} años", "@certifyAge": { @@ -4587,4 +4588,4 @@ "joinToView": "Únete a esta sala para ver los detalles", "autoPlayTitle": "Reproducción automática de mensajes", "autoPlayDesc": "Cuando está activado, el audio de texto a voz de los mensajes se reproducirá automáticamente cuando se seleccione." -} +} \ No newline at end of file diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 301e972f4..4ac171c83 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -341,7 +341,7 @@ class MessageContent extends StatelessWidget { ), onListen: () => toolbarController?.showToolbar( context, - mode: MessageMode.conversion, + mode: MessageMode.textToSpeech, ), ), enableInteractiveSelection: diff --git a/lib/pangea/constants/pangea_event_types.dart b/lib/pangea/constants/pangea_event_types.dart index abd155460..ce7c2373d 100644 --- a/lib/pangea/constants/pangea_event_types.dart +++ b/lib/pangea/constants/pangea_event_types.dart @@ -2,6 +2,8 @@ class PangeaEventTypes { static const classSettings = "pangea.class"; static const pangeaExchange = "p.exchange"; + static const transcript = "pangea.transcript"; + static const rules = "p.rules"; static const studentAnalyticsSummary = "pangea.usranalytics"; @@ -18,4 +20,6 @@ class PangeaEventTypes { static const botOptions = "pangea.bot_options"; static const userAge = "pangea.user_age"; + + static const String report = 'm.report'; } diff --git a/lib/pangea/constants/pangea_message_types.dart b/lib/pangea/constants/pangea_message_types.dart deleted file mode 100644 index 23ee52abd..000000000 --- a/lib/pangea/constants/pangea_message_types.dart +++ /dev/null @@ -1,3 +0,0 @@ -class PangeaMessageTypes { - static String report = 'm.report'; -} diff --git a/lib/pangea/controllers/speech_to_text_controller.dart b/lib/pangea/controllers/speech_to_text_controller.dart index c0eefb390..31902195f 100644 --- a/lib/pangea/controllers/speech_to_text_controller.dart +++ b/lib/pangea/controllers/speech_to_text_controller.dart @@ -1,8 +1,11 @@ import 'dart:async'; import 'dart:convert'; +import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; +import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/models/speech_to_text_models.dart'; +import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/foundation.dart'; import 'package:http/http.dart'; @@ -28,7 +31,7 @@ class SpeechToTextController { } void _initializeCacheClearing() { - const duration = Duration(minutes: 15); + const duration = Duration(minutes: 2); _cacheClearTimer = Timer.periodic(duration, (Timer t) => _clearCache()); } @@ -41,7 +44,8 @@ class SpeechToTextController { } Future get( - SpeechToTextRequestModel requestModel) async { + SpeechToTextRequestModel requestModel, + ) async { final int cacheKey = requestModel.hashCode; if (_cache.containsKey(cacheKey)) { @@ -52,11 +56,35 @@ class SpeechToTextController { requestModel: requestModel, ); _cache[cacheKey] = _SpeechToTextCacheItem(data: response); + return response; } } - static Future _fetchResponse({ + Future saveTranscriptAsMatrixEvent( + SpeechToTextResponseModel 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'); + final json = response.toJson(); + + requestModel.audioEvent?.room.sendPangeaEvent( + content: response.toJson(), + parentEventId: requestModel.audioEvent!.eventId, + type: PangeaEventTypes.transcript, + ); + debugPrint('Transcript saved as matrix event'); + + return Future.value(null); + } + + Future _fetchResponse({ required String accessToken, required SpeechToTextRequestModel requestModel, }) async { @@ -72,7 +100,14 @@ class SpeechToTextController { if (res.statusCode == 200) { final Map json = jsonDecode(utf8.decode(res.bodyBytes)); - return SpeechToTextResponseModel.fromJson(json); + + final response = SpeechToTextResponseModel.fromJson(json); + + saveTranscriptAsMatrixEvent(response, requestModel).onError( + (error, stackTrace) => ErrorHandler.logError(e: error, s: stackTrace), + ); + + return response; } else { debugPrint('Error converting speech to text: ${res.body}'); throw Exception('Failed to convert speech to text'); diff --git a/lib/pangea/enum/message_mode_enum.dart b/lib/pangea/enum/message_mode_enum.dart index e4d5c5c5d..22ce88a0f 100644 --- a/lib/pangea/enum/message_mode_enum.dart +++ b/lib/pangea/enum/message_mode_enum.dart @@ -1,15 +1,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -enum MessageMode { conversion, translation, definition } +enum MessageMode { translation, definition, speechToText, textToSpeech } extension MessageModeExtension on MessageMode { - IconData icon(bool isAudioMessage) { + IconData get icon { switch (this) { case MessageMode.translation: return Icons.g_translate; - case MessageMode.conversion: + case MessageMode.textToSpeech: return Icons.play_arrow; + case MessageMode.speechToText: + return Icons.mic; //TODO change icon for audio messages case MessageMode.definition: return Icons.book; @@ -22,8 +24,10 @@ extension MessageModeExtension on MessageMode { switch (this) { case MessageMode.translation: return L10n.of(context)!.translations; - case MessageMode.conversion: + case MessageMode.textToSpeech: return L10n.of(context)!.messageAudio; + case MessageMode.speechToText: + return L10n.of(context)!.transcriptTooltip; case MessageMode.definition: return L10n.of(context)!.definitions; default: @@ -36,8 +40,10 @@ extension MessageModeExtension on MessageMode { switch (this) { case MessageMode.translation: return L10n.of(context)!.translationTooltip; - case MessageMode.conversion: + case MessageMode.textToSpeech: return L10n.of(context)!.audioTooltip; + case MessageMode.speechToText: + return L10n.of(context)!.transcriptTooltip; case MessageMode.definition: return L10n.of(context)!.define; default: diff --git a/lib/pangea/models/pangea_message_event.dart b/lib/pangea/models/pangea_message_event.dart index ccc44e49a..652b3519e 100644 --- a/lib/pangea/models/pangea_message_event.dart +++ b/lib/pangea/models/pangea_message_event.dart @@ -2,14 +2,15 @@ import 'dart:convert'; import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; -import 'package:fluffychat/pangea/constants/pangea_message_types.dart'; import 'package:fluffychat/pangea/controllers/text_to_speech_controller.dart'; +import 'package:fluffychat/pangea/enum/audio_encoding_enum.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/models/choreo_record.dart'; import 'package:fluffychat/pangea/models/class_model.dart'; import 'package:fluffychat/pangea/models/message_data_models.dart'; import 'package:fluffychat/pangea/models/pangea_match_model.dart'; import 'package:fluffychat/pangea/models/pangea_representation_event.dart'; +import 'package:fluffychat/pangea/models/speech_to_text_models.dart'; import 'package:fluffychat/pangea/utils/bot_name.dart'; import 'package:fluffychat/pangea/widgets/chat/message_audio_card.dart'; import 'package:flutter/material.dart'; @@ -56,6 +57,8 @@ class PangeaMessageEvent { Room get room => _event.room; + bool get isAudioMessage => _event.messageType == MessageTypes.Audio; + Event? _latestEditCache; Event get _latestEdit => _latestEditCache ??= _event .aggregatedEvents( @@ -267,6 +270,78 @@ class PangeaMessageEvent { null; }).toSet(); + Set get transcriptionEvents => _event.aggregatedEvents( + timeline, + PangeaEventTypes.transcript, + ); + + Event? get transcriptionEvent => transcriptionEvents.lastOrNull; + + Future getSpeechToTextLocal() async { + if (transcriptionEvent == null) return null; + + return SpeechToTextResponseModel.fromJson( + transcriptionEvent!.content[PangeaEventTypes.transcript] + as Map, + ); + } + + Future getSpeechToTextGlobal( + String l1Code, + String l2Code, + ) async { + if (!isAudioMessage) { + ErrorHandler.logError( + e: 'Message is not an audio message ${_event.eventId}', + s: StackTrace.current, + data: _event.content, + ); + return null; + } + + if (transcriptionEvent != null) return getSpeechToTextLocal(); + + final matrixFile = await _event.downloadAndDecryptAttachment(); + // Pangea# + // File? file; + + // TODO: Test on mobile and see if we need this case, doeesn't seem so + // if (!kIsWeb) { + // final tempDir = await getTemporaryDirectory(); + // final fileName = Uri.encodeComponent( + // // #Pangea + // // widget.event.attachmentOrThumbnailMxcUrl()!.pathSegments.last, + // widget.messageEvent.event + // .attachmentOrThumbnailMxcUrl()! + // .pathSegments + // .last, + // // Pangea# + // ); + // file = File('${tempDir.path}/${fileName}_${matrixFile.name}'); + // await file.writeAsBytes(matrixFile.bytes); + // } + + // audioFile = file; + + final SpeechToTextResponseModel response = + await MatrixState.pangeaController.speechToText.get( + 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: 44100, + userL1: l1Code, + userL2: l2Code, + ), + ), + ); + + return response; + } + List? _representations; List get representations { if (_representations != null) return _representations!; @@ -431,6 +506,8 @@ class PangeaMessageEvent { ), ); }, + ).onError( + (error, stackTrace) => ErrorHandler.logError(e: error, s: stackTrace), ); return pangeaRep; @@ -456,7 +533,7 @@ class PangeaMessageEvent { _event.room.isSpaceAdmin && _event.senderId != BotName.byEnvironment && !room.isUserSpaceAdmin(_event.senderId) && - _event.messageType != PangeaMessageTypes.report; + _event.messageType != PangeaEventTypes.report; String get messageDisplayLangCode { final bool immersionMode = MatrixState diff --git a/lib/pangea/models/speech_to_text_models.dart b/lib/pangea/models/speech_to_text_models.dart index d55de33d9..83a04ec8b 100644 --- a/lib/pangea/models/speech_to_text_models.dart +++ b/lib/pangea/models/speech_to_text_models.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:fluffychat/pangea/enum/audio_encoding_enum.dart'; import 'package:flutter/foundation.dart'; +import 'package:matrix/matrix.dart'; class SpeechToTextAudioConfigModel { final AudioEncodingEnum encoding; @@ -33,10 +34,12 @@ class SpeechToTextAudioConfigModel { class SpeechToTextRequestModel { final Uint8List audioContent; final SpeechToTextAudioConfigModel config; + final Event? audioEvent; SpeechToTextRequestModel({ required this.audioContent, required this.config, + this.audioEvent, }); Map toJson() => { @@ -68,7 +71,7 @@ class WordInfo { final String word; final Duration? startTime; final Duration? endTime; - final double? confidence; + final int? confidence; WordInfo({ required this.word, @@ -85,13 +88,20 @@ class WordInfo { endTime: json['end_time'] != null ? Duration(milliseconds: json['end_time']) : null, - confidence: (json['confidence'] as num?)?.toDouble(), + confidence: json['confidence'], ); + + Map toJson() => { + "word": word, + "start_time": startTime?.inMilliseconds, + "end_time": endTime?.inMilliseconds, + "confidence": confidence, + }; } class Transcript { final String transcript; - final double confidence; + final int confidence; final List words; Transcript({ @@ -106,6 +116,12 @@ class Transcript { words: (json['words'] as List).map((e) => WordInfo.fromJson(e)).toList(), ); + + Map toJson() => { + "transcript": transcript, + "confidence": confidence, + "words": words.map((e) => e.toJson()).toList(), + }; } class SpeechToTextResult { @@ -119,6 +135,10 @@ class SpeechToTextResult { .map((e) => Transcript.fromJson(e)) .toList(), ); + + Map toJson() => { + "transcripts": transcripts.map((e) => e.toJson()).toList(), + }; } class SpeechToTextResponseModel { @@ -134,4 +154,8 @@ class SpeechToTextResponseModel { .map((e) => SpeechToTextResult.fromJson(e)) .toList(), ); + + Map toJson() => { + "results": results.map((e) => e.toJson()).toList(), + }; } diff --git a/lib/pangea/utils/report_message.dart b/lib/pangea/utils/report_message.dart index a7b3f1c28..5b6ceb0ba 100644 --- a/lib/pangea/utils/report_message.dart +++ b/lib/pangea/utils/report_message.dart @@ -1,12 +1,10 @@ -import 'package:flutter/material.dart'; - -import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:matrix/matrix.dart'; - -import 'package:fluffychat/pangea/constants/pangea_message_types.dart'; +import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/extensions/client_extension.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/widgets/matrix.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:matrix/matrix.dart'; Future reportMessage( BuildContext context, @@ -66,7 +64,7 @@ Future reportMessage( final String message = "$messageTitle\n\n$messageBody"; for (final Room reportDM in reportDMs) { final event = { - 'msgtype': PangeaMessageTypes.report, + 'msgtype': PangeaEventTypes.report, 'body': message, }; await reportDM.sendEvent(event); diff --git a/lib/pangea/widgets/chat/message_speech_to_text_card.dart b/lib/pangea/widgets/chat/message_speech_to_text_card.dart index 88b77e6cf..3a717349c 100644 --- a/lib/pangea/widgets/chat/message_speech_to_text_card.dart +++ b/lib/pangea/widgets/chat/message_speech_to_text_card.dart @@ -1,16 +1,10 @@ -import 'dart:developer'; - -import 'package:fluffychat/pangea/enum/audio_encoding_enum.dart'; -import 'package:fluffychat/pangea/models/message_data_models.dart'; import 'package:fluffychat/pangea/models/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/speech_to_text_models.dart'; import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; -import 'package:fluffychat/utils/localized_exception_extension.dart'; +import 'package:fluffychat/pangea/widgets/igc/card_error_widget.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:matrix/matrix.dart'; class MessageSpeechToTextCard extends StatefulWidget { final PangeaMessageEvent messageEvent; @@ -24,15 +18,10 @@ class MessageSpeechToTextCard extends StatefulWidget { MessageSpeechToTextCardState createState() => MessageSpeechToTextCardState(); } -enum AudioFileStatus { notDownloaded, downloading, downloaded } - class MessageSpeechToTextCardState extends State { - PangeaRepresentation? repEvent; - String? transcription; + SpeechToTextResponseModel? speechToTextResponse; bool _fetchingTranscription = true; - AudioFileStatus status = AudioFileStatus.notDownloaded; - MatrixFile? matrixFile; - // File? audioFile; + Object? error; String? get l1Code => MatrixState.pangeaController.languageController.activeL1Code( @@ -43,124 +32,41 @@ class MessageSpeechToTextCardState extends State { roomID: widget.messageEvent.room.id, ); - // get transcription from local events - Future getLocalTranscription() async { - return "This is a dummy transcription"; - } - - // This code is duplicated from audio_player.dart. Is there some way to reuse that code? - Future _downloadAction() async { - // #Pangea - // if (status != AudioFileStatus.notDownloaded) return; - if (status != AudioFileStatus.notDownloaded) { - return; - } - // Pangea# - setState(() => status = AudioFileStatus.downloading); - try { - // #Pangea - // final matrixFile = await widget.event.downloadAndDecryptAttachment(); - final matrixFile = - await widget.messageEvent.event.downloadAndDecryptAttachment(); - // Pangea# - // File? file; - - // TODO: Test on mobile and see if we need this case - // if (!kIsWeb) { - // final tempDir = await getTemporaryDirectory(); - // final fileName = Uri.encodeComponent( - // // #Pangea - // // widget.event.attachmentOrThumbnailMxcUrl()!.pathSegments.last, - // widget.messageEvent.event - // .attachmentOrThumbnailMxcUrl()! - // .pathSegments - // .last, - // // Pangea# - // ); - // file = File('${tempDir.path}/${fileName}_${matrixFile.name}'); - // await file.writeAsBytes(matrixFile.bytes); - // } - - // audioFile = file; - this.matrixFile = matrixFile; - status = AudioFileStatus.downloaded; - } catch (e, s) { - Logs().v('Could not download audio file', e, s); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(e.toLocalizedString(context)), - ), - ); - } - } - - AudioEncodingEnum? get encoding { - if (matrixFile == null) return null; - return mimeTypeToAudioEncoding(matrixFile!.mimeType); - } - - // call API to transcribe audio - Future transcribeAudio() async { - await _downloadAction(); - - final localmatrixFile = matrixFile; - final info = matrixFile?.info; - - if (matrixFile == null) { - debugger(when: kDebugMode); - ErrorHandler.logError( - e: 'Audio file or matrix file is null ${widget.messageEvent.eventId}', - s: StackTrace.current, - data: widget.messageEvent.event.content, - ); - return null; - } - - if (l1Code == null || l2Code == null) { - debugger(when: kDebugMode); - ErrorHandler.logError( - e: 'Language codes are null ${widget.messageEvent.eventId}', - s: StackTrace.current, - data: widget.messageEvent.event.content, - ); - return null; - } - - final SpeechToTextResponseModel response = - await MatrixState.pangeaController.speechToText.get( - SpeechToTextRequestModel( - audioContent: matrixFile!.bytes, - config: SpeechToTextAudioConfigModel( - encoding: encoding ?? AudioEncodingEnum.encodingUnspecified, - //this is the default in the RecordConfig in record package - sampleRateHertz: 44100, - userL1: l1Code!, - userL2: l2Code!, - ), - ), - ); - return response.results.first.transcripts.first.transcript; - } + String? get transcription => speechToTextResponse + ?.results.firstOrNull?.transcripts.firstOrNull?.transcript; // look for transcription in message event // if not found, call API to transcribe audio - Future loadTranscription() async { - // transcription ??= await getLocalTranscription(); - transcription ??= await transcribeAudio(); - setState(() => _fetchingTranscription = false); + Future getSpeechToText() async { + try { + if (l1Code == null || l2Code == null) { + throw Exception('Language selection not found'); + } + speechToTextResponse ??= + await widget.messageEvent.getSpeechToTextGlobal(l1Code!, l2Code!); + } catch (e, s) { + error = e; + ErrorHandler.logError( + e: e, + s: s, + data: widget.messageEvent.event.content, + ); + } finally { + setState(() => _fetchingTranscription = false); + } } @override void initState() { super.initState(); - loadTranscription(); + getSpeechToText(); } @override Widget build(BuildContext context) { - // if (!_fetchingTranscription && repEvent == null && transcription == null) { - // return const CardErrorWidget(); - // } + if (!_fetchingTranscription && speechToTextResponse == null) { + return CardErrorWidget(error: error); + } return Padding( padding: const EdgeInsets.all(8), @@ -173,15 +79,10 @@ class MessageSpeechToTextCardState extends State { color: Theme.of(context).colorScheme.primary, ), ) - : transcription != null - ? Text( - transcription!, - style: BotStyle.text(context), - ) - : Text( - repEvent!.text, - style: BotStyle.text(context), - ), + : Text( + transcription!, + style: BotStyle.text(context), + ), ); } } diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 1a722dac1..196416422 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -176,13 +176,21 @@ class MessageToolbarState extends State { case MessageMode.translation: showTranslation(); break; - case MessageMode.conversion: - showConversion(); + case MessageMode.textToSpeech: + showTextToSpeech(); + break; + case MessageMode.speechToText: + showSpeechToText(); break; case MessageMode.definition: showDefinition(); break; default: + ErrorHandler.logError( + e: "Invalid toolbar mode", + s: StackTrace.current, + data: {"newMode": newMode}, + ); break; } } @@ -200,21 +208,22 @@ class MessageToolbarState extends State { ); } - void showConversion() { - debugPrint("show conversion"); - if (isAudioMessage) { - debugPrint("is audio message"); - toolbarContent = MessageSpeechToTextCard( - messageEvent: widget.pangeaMessageEvent, - ); - } else { - toolbarContent = MessageAudioCard( - messageEvent: widget.pangeaMessageEvent, - ); - } + void showTextToSpeech() { + debugPrint("show text to speech"); + toolbarContent = MessageAudioCard( + messageEvent: widget.pangeaMessageEvent, + ); + } + + void showSpeechToText() { + debugPrint("show speech to text"); + toolbarContent = MessageSpeechToTextCard( + messageEvent: widget.pangeaMessageEvent, + ); } void showDefinition() { + debugPrint("show definition"); if (widget.textSelection.selectedText == null || widget.textSelection.selectedText!.isEmpty) { toolbarContent = const SelectToDefine(); @@ -231,10 +240,6 @@ class MessageToolbarState extends State { ); } - bool get isAudioMessage { - return widget.pangeaMessageEvent.event.messageType == MessageTypes.Audio; - } - void showImage() {} void spellCheck() {} @@ -259,7 +264,9 @@ class MessageToolbarState extends State { ) ?? true; autoplay - ? updateMode(MessageMode.conversion) + ? updateMode(widget.pangeaMessageEvent.isAudioMessage + ? MessageMode.speechToText + : MessageMode.textToSpeech) : updateMode(MessageMode.translation); }); @@ -322,13 +329,19 @@ class MessageToolbarState extends State { Row( mainAxisSize: MainAxisSize.min, children: MessageMode.values.map((mode) { - if (mode == MessageMode.definition && isAudioMessage) { + if ([MessageMode.definition, MessageMode.textToSpeech] + .contains(mode) && + widget.pangeaMessageEvent.isAudioMessage) { + return const SizedBox.shrink(); + } + if (mode == MessageMode.speechToText && + !widget.pangeaMessageEvent.isAudioMessage) { return const SizedBox.shrink(); } return Tooltip( message: mode.tooltip(context), child: IconButton( - icon: Icon(mode.icon(isAudioMessage)), + icon: Icon(mode.icon), color: currentMode == mode ? Theme.of(context).colorScheme.primary : null, diff --git a/lib/pangea/widgets/igc/pangea_rich_text.dart b/lib/pangea/widgets/igc/pangea_rich_text.dart index e083a67b6..612fabe8d 100644 --- a/lib/pangea/widgets/igc/pangea_rich_text.dart +++ b/lib/pangea/widgets/igc/pangea_rich_text.dart @@ -87,7 +87,8 @@ class PangeaRichTextState extends State { context: context, langCode: widget.pangeaMessageEvent.messageDisplayLangCode, ) - .onError((error, stackTrace) => ErrorHandler.logError()) + .onError((error, stackTrace) => + ErrorHandler.logError(e: error, s: stackTrace)) .then((event) { repEvent = event; widget.toolbarController?.toolbar?.textSelection.setMessageText( @@ -158,7 +159,7 @@ class PangeaRichTextState extends State { ), onListen: () => widget.toolbarController?.showToolbar( context, - mode: MessageMode.conversion, + mode: MessageMode.textToSpeech, ), ), TextSpan( diff --git a/needed-translations.txt b/needed-translations.txt index b5d0fb74a..51e52cf0f 100644 --- a/needed-translations.txt +++ b/needed-translations.txt @@ -105,7 +105,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -154,10 +153,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -176,6 +171,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -195,7 +195,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -705,11 +704,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -759,13 +758,13 @@ "reportMessageTitle", "reportMessageBody", "noTeachersFound", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", @@ -784,6 +783,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -829,6 +829,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -947,7 +948,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -996,10 +996,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -1018,6 +1014,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -1037,7 +1038,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -1547,11 +1547,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -1604,21 +1604,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -1637,6 +1637,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -1682,6 +1683,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -1800,7 +1802,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -1849,10 +1850,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -1871,6 +1868,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -1890,7 +1892,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -2400,11 +2401,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -2457,21 +2458,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -2490,6 +2491,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -2535,6 +2537,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -2653,7 +2656,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -2702,10 +2704,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -2724,6 +2722,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -2743,7 +2746,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -3253,11 +3255,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -3310,21 +3312,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -3343,6 +3345,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -3388,6 +3391,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -3506,7 +3510,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -3555,10 +3558,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -3577,6 +3576,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -3596,7 +3600,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -4106,11 +4109,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -4163,21 +4166,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -4196,6 +4199,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -4241,6 +4245,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -4355,7 +4360,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -4404,10 +4408,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -4426,6 +4426,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -4445,7 +4450,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -4955,6 +4959,7 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", @@ -5008,13 +5013,13 @@ "reportMessageTitle", "reportMessageBody", "noTeachersFound", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", @@ -5063,6 +5068,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -5181,7 +5187,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -5230,10 +5235,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -5252,6 +5253,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -5271,7 +5277,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -5781,11 +5786,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -5838,21 +5843,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -5871,6 +5876,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -5916,6 +5922,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -6034,7 +6041,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -6083,10 +6089,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -6105,6 +6107,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -6124,7 +6131,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -6634,11 +6640,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -6691,21 +6697,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -6724,6 +6730,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -6769,6 +6776,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -6883,7 +6891,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -6932,10 +6939,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -6954,6 +6957,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -6973,7 +6981,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -7483,11 +7490,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -7537,13 +7544,13 @@ "reportMessageTitle", "reportMessageBody", "noTeachersFound", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", @@ -7562,6 +7569,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -7607,6 +7615,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -7721,7 +7730,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -7770,10 +7778,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -7792,6 +7796,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -7811,7 +7820,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -8321,6 +8329,7 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", @@ -8374,13 +8383,13 @@ "reportMessageTitle", "reportMessageBody", "noTeachersFound", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", @@ -8429,6 +8438,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -8547,7 +8557,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -8596,10 +8605,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -8618,6 +8623,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -8637,7 +8647,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -9147,11 +9156,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -9204,21 +9213,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -9237,6 +9246,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -9282,6 +9292,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -9400,7 +9411,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -9449,10 +9459,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -9471,6 +9477,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -9490,7 +9501,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -10000,11 +10010,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -10057,21 +10067,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -10090,6 +10100,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -10135,6 +10146,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -10253,7 +10265,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -10302,10 +10313,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -10324,6 +10331,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -10343,7 +10355,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -10853,11 +10864,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -10910,21 +10921,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -10943,6 +10954,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -10988,6 +11000,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -11106,7 +11119,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -11155,10 +11167,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -11177,6 +11185,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -11196,7 +11209,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -11706,11 +11718,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -11763,21 +11775,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -11796,6 +11808,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -11841,6 +11854,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -11955,7 +11969,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -12004,10 +12017,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -12026,6 +12035,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -12045,7 +12059,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -12555,6 +12568,7 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", @@ -12608,13 +12622,13 @@ "reportMessageTitle", "reportMessageBody", "noTeachersFound", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", @@ -12663,6 +12677,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -12781,7 +12796,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -12830,10 +12844,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -12852,6 +12862,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -12871,7 +12886,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -13381,11 +13395,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -13438,21 +13452,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -13471,6 +13485,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -13516,6 +13531,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -13634,7 +13650,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -13683,10 +13698,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -13705,6 +13716,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -13724,7 +13740,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -14234,11 +14249,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -14291,21 +14306,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -14324,6 +14339,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -14369,6 +14385,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -14484,7 +14501,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -14533,10 +14549,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -14555,6 +14567,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -14574,7 +14591,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -15084,11 +15100,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -15139,13 +15155,13 @@ "reportMessageBody", "noTeachersFound", "noUsersFoundWithQuery", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", @@ -15164,6 +15180,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -15209,6 +15226,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -15327,7 +15345,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -15376,10 +15393,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -15398,6 +15411,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -15417,7 +15435,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -15927,11 +15944,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -15984,21 +16001,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -16017,6 +16034,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -16062,6 +16080,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -16180,7 +16199,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -16229,10 +16247,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -16251,6 +16265,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -16270,7 +16289,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -16780,11 +16798,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -16837,21 +16855,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -16870,6 +16888,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -16915,6 +16934,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -17033,7 +17053,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -17082,10 +17101,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -17104,6 +17119,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -17123,7 +17143,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -17633,11 +17652,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -17690,21 +17709,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -17723,6 +17742,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -17768,6 +17788,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -17882,7 +17903,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -17931,10 +17951,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -17953,6 +17969,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -17972,7 +17993,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -18482,11 +18502,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -18536,13 +18556,13 @@ "reportMessageTitle", "reportMessageBody", "noTeachersFound", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", @@ -18561,6 +18581,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -18606,6 +18627,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -18724,7 +18746,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -18773,10 +18794,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -18795,6 +18812,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -18814,7 +18836,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -19324,11 +19345,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -19381,21 +19402,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -19414,6 +19435,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -19459,6 +19481,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -19577,7 +19600,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -19626,10 +19648,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -19648,6 +19666,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -19667,7 +19690,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -20177,11 +20199,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -20234,21 +20256,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -20267,6 +20289,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -20312,6 +20335,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -20430,7 +20454,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -20479,10 +20502,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -20501,6 +20520,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -20520,7 +20544,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -21030,11 +21053,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -21087,21 +21110,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -21120,6 +21143,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -21165,6 +21189,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -21283,7 +21308,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -21332,10 +21356,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -21354,6 +21374,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -21373,7 +21398,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -21883,11 +21907,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -21940,21 +21964,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -21973,6 +21997,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -22018,6 +22043,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -22136,7 +22162,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -22185,10 +22210,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -22207,6 +22228,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -22226,7 +22252,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -22736,11 +22761,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -22793,21 +22818,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -22826,6 +22851,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -22871,6 +22897,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -22989,7 +23016,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -23038,10 +23064,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -23060,6 +23082,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -23079,7 +23106,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -23589,11 +23615,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -23646,21 +23672,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -23679,6 +23705,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -23724,6 +23751,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -23842,7 +23870,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -23891,10 +23918,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -23913,6 +23936,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -23932,7 +23960,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -24442,11 +24469,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -24499,21 +24526,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -24532,6 +24559,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -24577,6 +24605,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -24695,7 +24724,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -24744,10 +24772,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -24766,6 +24790,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -24785,7 +24814,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -25295,11 +25323,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -25352,21 +25380,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -25385,6 +25413,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -25430,6 +25459,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -25544,7 +25574,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -25593,10 +25622,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -25615,6 +25640,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -25634,7 +25664,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -26144,6 +26173,7 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", @@ -26197,13 +26227,13 @@ "reportMessageTitle", "reportMessageBody", "noTeachersFound", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", @@ -26252,6 +26282,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -26370,7 +26401,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -26419,10 +26449,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -26441,6 +26467,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -26460,7 +26491,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -26970,11 +27000,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -27027,21 +27057,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -27060,6 +27090,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -27105,6 +27136,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -27223,7 +27255,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -27272,10 +27303,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -27294,6 +27321,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -27313,7 +27345,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -27823,11 +27854,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -27880,21 +27911,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -27913,6 +27944,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -27958,6 +27990,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -28072,7 +28105,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -28121,10 +28153,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -28143,6 +28171,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -28162,7 +28195,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -28672,11 +28704,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -28726,19 +28758,20 @@ "reportMessageTitle", "reportMessageBody", "noTeachersFound", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -28784,6 +28817,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -28902,7 +28936,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -28951,10 +28984,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -28973,6 +29002,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -28992,7 +29026,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -29502,11 +29535,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -29559,21 +29592,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -29592,6 +29625,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -29637,6 +29671,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -29755,7 +29790,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -29804,10 +29838,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -29826,6 +29856,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -29845,7 +29880,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -30355,11 +30389,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -30412,21 +30446,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -30445,6 +30479,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -30490,6 +30525,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -30608,7 +30644,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -30657,10 +30692,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -30679,6 +30710,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -30698,7 +30734,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -31208,11 +31243,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -31265,21 +31300,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -31298,6 +31333,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -31343,6 +31379,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -31461,7 +31498,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -31510,10 +31546,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -31532,6 +31564,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -31551,7 +31588,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -32061,11 +32097,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -32118,21 +32154,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -32151,6 +32187,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -32196,6 +32233,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -32314,7 +32352,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -32363,10 +32400,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -32385,6 +32418,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -32404,7 +32442,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -32914,11 +32951,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -32971,21 +33008,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -33004,6 +33041,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -33049,6 +33087,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -33167,7 +33206,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -33216,10 +33254,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -33238,6 +33272,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -33257,7 +33296,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -33767,11 +33805,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -33824,21 +33862,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -33857,6 +33895,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -33902,6 +33941,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -34016,7 +34056,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -34065,10 +34104,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -34087,6 +34122,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -34106,7 +34146,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -34616,11 +34655,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -34670,13 +34709,13 @@ "reportMessageTitle", "reportMessageBody", "noTeachersFound", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", @@ -34695,6 +34734,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -34740,6 +34780,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -34854,7 +34895,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -34903,10 +34943,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -34925,6 +34961,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -34944,7 +34985,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -35454,11 +35494,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -35508,13 +35548,13 @@ "reportMessageTitle", "reportMessageBody", "noTeachersFound", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", @@ -35533,6 +35573,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -35578,6 +35619,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -35696,7 +35738,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -35745,10 +35786,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -35767,6 +35804,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -35786,7 +35828,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -36296,11 +36337,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -36353,21 +36394,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -36386,6 +36427,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -36431,6 +36473,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -36545,7 +36588,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -36594,10 +36636,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -36616,6 +36654,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -36635,7 +36678,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -37145,11 +37187,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -37199,13 +37241,13 @@ "reportMessageTitle", "reportMessageBody", "noTeachersFound", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", @@ -37224,6 +37266,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -37269,6 +37312,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -37387,7 +37431,6 @@ "getStarted", "mustBe13", "yourBirthdayPleaseShort", - "errorPleaseRefresh", "joinWithClassCode", "joinWithClassCodeDesc", "joinWithClassCodeHint", @@ -37436,10 +37479,6 @@ "saveChanges", "publicProfileTitle", "publicProfileDesc", - "error502504Title", - "error502504Desc", - "error404Title", - "error404Desc", "errorDisableIT", "errorDisableIGC", "errorDisableLanguageAssistance", @@ -37458,6 +37497,11 @@ "iAgreeToThe", "termsAndConditions", "andCertifyIAmAtLeast13YearsOfAge", + "error502504Title", + "error502504Desc", + "error404Title", + "error404Desc", + "errorPleaseRefresh", "findAClass", "toggleIT", "toggleIGC", @@ -37477,7 +37521,6 @@ "clickMessageTitle", "clickMessageBody", "understandingMessagesTitle", - "addToClass", "understandingMessagesBody", "allDone", "vocab", @@ -37987,11 +38030,11 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", + "addToClass", "addToClassDesc", "addToClassOrExchange", "addToClassOrExchangeDesc", "invitedToClassOrExchange", - "decline", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -38044,21 +38087,21 @@ "yourGlobalUserIdIs", "noUsersFoundWithQuery", "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", "createClass", "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", "activateTrial", + "inNoSpaces", "successfullySubscribed", "clickToManageSubscription", "emptyInviteWarning", "errorGettingAudio", "nothingFound", + "groupName", + "createGroupAndInviteUsers", + "groupCanBeFoundViaSearch", "wrongRecoveryKey", "startConversation", "commandHint_sendraw", @@ -38077,6 +38120,7 @@ "publicSpaces", "addChatOrSubSpace", "subspace", + "decline", "thisDevice", "initAppError", "databaseBuildErrorBody", @@ -38122,6 +38166,7 @@ "more", "translationTooltip", "audioTooltip", + "transcriptTooltip", "certifyAge", "kickBotWarning", "joinToView", From 4a77e6fb53066f0a598cb2aff30e7bb001caf167 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Mon, 6 May 2024 15:10:15 -0400 Subject: [PATCH 3/7] full draft speech to text with word level feedback --- assets/l10n/intl_en.arb | 2 +- assets/l10n/intl_es.arb | 2 +- lib/pages/chat/chat.dart | 5 +- lib/pages/chat/events/message.dart | 2 +- lib/pages/chat/events/message_content.dart | 2 +- .../controllers/choreographer.dart | 3 +- .../message_analytics_controller.dart | 2 +- .../controllers/message_data_controller.dart | 3 +- .../speech_to_text_controller.dart | 30 ++- lib/pangea/enum/audio_encoding_enum.dart | 2 + lib/pangea/enum/message_mode_enum.dart | 4 +- .../extensions/pangea_event_extension.dart | 9 +- .../extensions/pangea_room_extension.dart | 7 +- .../construct_analytics_event.dart | 0 .../pangea_audio_events.dart | 0 .../pangea_choreo_event.dart | 9 +- .../pangea_message_event.dart | 49 ++-- .../pangea_representation_event.dart | 15 +- .../pangea_tokens_event.dart | 6 +- ...dart => representation_content_model.dart} | 67 +++--- lib/pangea/models/speech_to_text_models.dart | 68 ++++-- .../models/tokens_event_content_model.dart | 32 +++ .../pages/analytics/construct_list.dart | 6 +- lib/pangea/utils/bot_style.dart | 4 +- lib/pangea/utils/download_chat.dart | 2 +- .../utils/get_chat_list_item_subtitle.dart | 2 +- .../widgets/chat/message_audio_card.dart | 15 +- .../chat/message_speech_to_text_card.dart | 47 ++-- lib/pangea/widgets/chat/message_toolbar.dart | 17 +- .../chat/message_translation_card.dart | 17 +- .../chat/message_unsubscribed_card.dart | 47 ++-- lib/pangea/widgets/chat/overlay_message.dart | 2 +- .../widgets/chat/speech_to_text_score.dart | 29 +++ .../widgets/chat/speech_to_text_text.dart | 65 ++++++ .../widgets/chat/text_to_speech_button.dart | 2 +- .../toolbar_content_loading_indicator.dart | 19 ++ lib/pangea/widgets/igc/pangea_rich_text.dart | 10 +- macos/Podfile.lock | 218 ++++++++++++++++-- needed-translations.txt | 90 ++++---- 39 files changed, 637 insertions(+), 274 deletions(-) rename lib/pangea/{models => matrix_event_wrappers}/construct_analytics_event.dart (100%) rename lib/pangea/{models => matrix_event_wrappers}/pangea_audio_events.dart (100%) rename lib/pangea/{models => matrix_event_wrappers}/pangea_choreo_event.dart (97%) rename lib/pangea/{models => matrix_event_wrappers}/pangea_message_event.dart (94%) rename lib/pangea/{models => matrix_event_wrappers}/pangea_representation_event.dart (86%) rename lib/pangea/{models => matrix_event_wrappers}/pangea_tokens_event.dart (91%) rename lib/pangea/models/{message_data_models.dart => representation_content_model.dart} (66%) create mode 100644 lib/pangea/models/tokens_event_content_model.dart create mode 100644 lib/pangea/widgets/chat/speech_to_text_score.dart create mode 100644 lib/pangea/widgets/chat/speech_to_text_text.dart create mode 100644 lib/pangea/widgets/chat/toolbar_content_loading_indicator.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 28107dc47..9f6189acb 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -3887,7 +3887,7 @@ "more": "More", "translationTooltip": "Translate", "audioTooltip": "Play Audio", - "transcriptTooltip": "Transcript", + "speechToTextTooltip": "Transcript", "certifyAge": "I certify that I am over {age} years of age", "@certifyAge": { "type": "text", diff --git a/assets/l10n/intl_es.arb b/assets/l10n/intl_es.arb index 723a3d7a2..f9f596f16 100644 --- a/assets/l10n/intl_es.arb +++ b/assets/l10n/intl_es.arb @@ -4572,7 +4572,7 @@ "more": "Más", "translationTooltip": "Traducir", "audioTooltip": "Reproducir audio", - "transcriptTooltip": "Transcripción", + "speechToTextTooltip": "Transcripción", "yourBirthdayPleaseShort": "Seleccione su grupo de edad", "certifyAge": "Certifico que soy mayor de {age} años", "@certifyAge": { diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 67e459e03..b55bfa6e4 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -17,11 +17,12 @@ import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/enum/use_type.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/choreo_record.dart'; import 'package:fluffychat/pangea/models/class_model.dart'; -import 'package:fluffychat/pangea/models/message_data_models.dart'; -import 'package:fluffychat/pangea/models/pangea_message_event.dart'; +import 'package:fluffychat/pangea/models/representation_content_model.dart'; import 'package:fluffychat/pangea/models/student_analytics_summary_model.dart'; +import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/firebase_analytics.dart'; import 'package:fluffychat/pangea/utils/report_message.dart'; diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 7ce2c9cc3..52049c0a0 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -1,8 +1,8 @@ import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pangea/enum/use_type.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/language_model.dart'; -import 'package:fluffychat/pangea/models/pangea_message_event.dart'; import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/string_color.dart'; diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 4ac171c83..909fab21e 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -1,7 +1,7 @@ import 'package:fluffychat/pages/chat/events/html_message.dart'; import 'package:fluffychat/pages/chat/events/video_player.dart'; import 'package:fluffychat/pangea/enum/message_mode_enum.dart'; -import 'package:fluffychat/pangea/models/pangea_message_event.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/widgets/chat/message_context_menu.dart'; import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; import 'package:fluffychat/pangea/widgets/igc/pangea_rich_text.dart'; diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart index 162c75393..6da4831cc 100644 --- a/lib/pangea/choreographer/controllers/choreographer.dart +++ b/lib/pangea/choreographer/controllers/choreographer.dart @@ -13,7 +13,8 @@ import 'package:fluffychat/pangea/enum/edit_type.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/models/class_model.dart'; import 'package:fluffychat/pangea/models/it_step.dart'; -import 'package:fluffychat/pangea/models/message_data_models.dart'; +import 'package:fluffychat/pangea/models/representation_content_model.dart'; +import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/utils/any_state_holder.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/overlay.dart'; diff --git a/lib/pangea/controllers/message_analytics_controller.dart b/lib/pangea/controllers/message_analytics_controller.dart index d20e0bf36..563730a80 100644 --- a/lib/pangea/controllers/message_analytics_controller.dart +++ b/lib/pangea/controllers/message_analytics_controller.dart @@ -13,8 +13,8 @@ import 'package:matrix/matrix.dart'; import '../constants/class_default_values.dart'; import '../extensions/client_extension.dart'; import '../extensions/pangea_room_extension.dart'; +import '../matrix_event_wrappers/construct_analytics_event.dart'; import '../models/chart_analytics_model.dart'; -import '../models/construct_analytics_event.dart'; import '../models/student_analytics_event.dart'; import 'base_controller.dart'; import 'pangea_controller.dart'; diff --git a/lib/pangea/controllers/message_data_controller.dart b/lib/pangea/controllers/message_data_controller.dart index b0759b108..958956e2f 100644 --- a/lib/pangea/controllers/message_data_controller.dart +++ b/lib/pangea/controllers/message_data_controller.dart @@ -2,7 +2,8 @@ import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/controllers/base_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/models/message_data_models.dart'; +import 'package:fluffychat/pangea/models/representation_content_model.dart'; +import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/repo/tokens_repo.dart'; import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; diff --git a/lib/pangea/controllers/speech_to_text_controller.dart b/lib/pangea/controllers/speech_to_text_controller.dart index 31902195f..6b302b101 100644 --- a/lib/pangea/controllers/speech_to_text_controller.dart +++ b/lib/pangea/controllers/speech_to_text_controller.dart @@ -4,6 +4,7 @@ import 'dart:convert'; import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; +import 'package:fluffychat/pangea/models/representation_content_model.dart'; import 'package:fluffychat/pangea/models/speech_to_text_models.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/foundation.dart'; @@ -13,10 +14,10 @@ import '../config/environment.dart'; import '../network/requests.dart'; import '../network/urls.dart'; -// Assuming SpeechToTextRequestModel, SpeechToTextResponseModel and related models are already defined as in your provided code. +// Assuming SpeechToTextRequestModel, SpeechToTextModel and related models are already defined as in your provided code. class _SpeechToTextCacheItem { - Future data; + Future data; _SpeechToTextCacheItem({required this.data}); } @@ -43,7 +44,7 @@ class SpeechToTextController { _cacheClearTimer?.cancel(); } - Future get( + Future get( SpeechToTextRequestModel requestModel, ) async { final int cacheKey = requestModel.hashCode; @@ -51,7 +52,7 @@ class SpeechToTextController { if (_cache.containsKey(cacheKey)) { return _cache[cacheKey]!.data; } else { - final Future response = _fetchResponse( + final Future response = _fetchResponse( accessToken: await _pangeaController.userController.accessToken, requestModel: requestModel, ); @@ -61,8 +62,8 @@ class SpeechToTextController { } } - Future saveTranscriptAsMatrixEvent( - SpeechToTextResponseModel response, + Future saveSpeechToTextAsRepresentationEvent( + SpeechToTextModel response, SpeechToTextRequestModel requestModel, ) { if (requestModel.audioEvent == null) { @@ -72,19 +73,24 @@ class SpeechToTextController { return Future.value(null); } debugPrint('Saving transcript as matrix event'); - final json = response.toJson(); requestModel.audioEvent?.room.sendPangeaEvent( - content: response.toJson(), + content: PangeaRepresentation( + langCode: response.langCode, + text: response.transcript.text, + originalSent: false, + originalWritten: false, + speechToText: response, + ).toJson(), parentEventId: requestModel.audioEvent!.eventId, - type: PangeaEventTypes.transcript, + type: PangeaEventTypes.representation, ); debugPrint('Transcript saved as matrix event'); return Future.value(null); } - Future _fetchResponse({ + Future _fetchResponse({ required String accessToken, required SpeechToTextRequestModel requestModel, }) async { @@ -101,9 +107,9 @@ class SpeechToTextController { if (res.statusCode == 200) { final Map json = jsonDecode(utf8.decode(res.bodyBytes)); - final response = SpeechToTextResponseModel.fromJson(json); + final response = SpeechToTextModel.fromJson(json); - saveTranscriptAsMatrixEvent(response, requestModel).onError( + saveSpeechToTextAsRepresentationEvent(response, requestModel).onError( (error, stackTrace) => ErrorHandler.logError(e: error, s: stackTrace), ); diff --git a/lib/pangea/enum/audio_encoding_enum.dart b/lib/pangea/enum/audio_encoding_enum.dart index 729dec147..265e2315c 100644 --- a/lib/pangea/enum/audio_encoding_enum.dart +++ b/lib/pangea/enum/audio_encoding_enum.dart @@ -6,6 +6,8 @@ AudioEncodingEnum mimeTypeToAudioEncoding(String mimeType) { return AudioEncodingEnum.mp4; case 'audio/ogg': return AudioEncodingEnum.oggOpus; + case 'audio/x-flac': + return AudioEncodingEnum.flac; default: return AudioEncodingEnum.encodingUnspecified; } diff --git a/lib/pangea/enum/message_mode_enum.dart b/lib/pangea/enum/message_mode_enum.dart index 22ce88a0f..e07ee09b7 100644 --- a/lib/pangea/enum/message_mode_enum.dart +++ b/lib/pangea/enum/message_mode_enum.dart @@ -27,7 +27,7 @@ extension MessageModeExtension on MessageMode { case MessageMode.textToSpeech: return L10n.of(context)!.messageAudio; case MessageMode.speechToText: - return L10n.of(context)!.transcriptTooltip; + return L10n.of(context)!.speechToTextTooltip; case MessageMode.definition: return L10n.of(context)!.definitions; default: @@ -43,7 +43,7 @@ extension MessageModeExtension on MessageMode { case MessageMode.textToSpeech: return L10n.of(context)!.audioTooltip; case MessageMode.speechToText: - return L10n.of(context)!.transcriptTooltip; + return L10n.of(context)!.speechToTextTooltip; case MessageMode.definition: return L10n.of(context)!.define; default: diff --git a/lib/pangea/extensions/pangea_event_extension.dart b/lib/pangea/extensions/pangea_event_extension.dart index 9d4556721..f62f27925 100644 --- a/lib/pangea/extensions/pangea_event_extension.dart +++ b/lib/pangea/extensions/pangea_event_extension.dart @@ -1,12 +1,11 @@ import 'dart:developer'; -import 'package:flutter/foundation.dart'; - -import 'package:matrix/matrix.dart'; - import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/models/choreo_record.dart'; -import 'package:fluffychat/pangea/models/message_data_models.dart'; +import 'package:fluffychat/pangea/models/representation_content_model.dart'; +import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; +import 'package:flutter/foundation.dart'; +import 'package:matrix/matrix.dart'; extension PangeaEvent on Event { V getPangeaContent() { diff --git a/lib/pangea/extensions/pangea_room_extension.dart b/lib/pangea/extensions/pangea_room_extension.dart index e617247de..9684a28a1 100644 --- a/lib/pangea/extensions/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension.dart @@ -4,9 +4,10 @@ import 'dart:developer'; import 'package:fluffychat/pangea/constants/class_default_values.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/constants/pangea_room_types.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/bot_options_model.dart'; import 'package:fluffychat/pangea/models/class_model.dart'; -import 'package:fluffychat/pangea/models/pangea_message_event.dart'; +import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/utils/bot_name.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/foundation.dart'; @@ -22,10 +23,10 @@ import '../../config/app_config.dart'; import '../constants/pangea_event_types.dart'; import '../enum/construct_type_enum.dart'; import '../enum/use_type.dart'; +import '../matrix_event_wrappers/construct_analytics_event.dart'; import '../models/choreo_record.dart'; -import '../models/construct_analytics_event.dart'; import '../models/constructs_analytics_model.dart'; -import '../models/message_data_models.dart'; +import '../models/representation_content_model.dart'; import '../models/student_analytics_event.dart'; import '../models/student_analytics_summary_model.dart'; import '../utils/p_store.dart'; diff --git a/lib/pangea/models/construct_analytics_event.dart b/lib/pangea/matrix_event_wrappers/construct_analytics_event.dart similarity index 100% rename from lib/pangea/models/construct_analytics_event.dart rename to lib/pangea/matrix_event_wrappers/construct_analytics_event.dart diff --git a/lib/pangea/models/pangea_audio_events.dart b/lib/pangea/matrix_event_wrappers/pangea_audio_events.dart similarity index 100% rename from lib/pangea/models/pangea_audio_events.dart rename to lib/pangea/matrix_event_wrappers/pangea_audio_events.dart diff --git a/lib/pangea/models/pangea_choreo_event.dart b/lib/pangea/matrix_event_wrappers/pangea_choreo_event.dart similarity index 97% rename from lib/pangea/models/pangea_choreo_event.dart rename to lib/pangea/matrix_event_wrappers/pangea_choreo_event.dart index f28f04b04..47a6b688f 100644 --- a/lib/pangea/models/pangea_choreo_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_choreo_event.dart @@ -1,11 +1,10 @@ -import 'package:flutter/foundation.dart'; - -import 'package:matrix/matrix.dart'; - import 'package:fluffychat/pangea/extensions/pangea_event_extension.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:flutter/foundation.dart'; +import 'package:matrix/matrix.dart'; + import '../constants/pangea_event_types.dart'; -import 'choreo_record.dart'; +import '../models/choreo_record.dart'; class ChoreoEvent { Event event; diff --git a/lib/pangea/models/pangea_message_event.dart b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart similarity index 94% rename from lib/pangea/models/pangea_message_event.dart rename to lib/pangea/matrix_event_wrappers/pangea_message_event.dart index 652b3519e..0b582a88d 100644 --- a/lib/pangea/models/pangea_message_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart @@ -5,12 +5,13 @@ import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/controllers/text_to_speech_controller.dart'; import 'package:fluffychat/pangea/enum/audio_encoding_enum.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_representation_event.dart'; import 'package:fluffychat/pangea/models/choreo_record.dart'; import 'package:fluffychat/pangea/models/class_model.dart'; -import 'package:fluffychat/pangea/models/message_data_models.dart'; import 'package:fluffychat/pangea/models/pangea_match_model.dart'; -import 'package:fluffychat/pangea/models/pangea_representation_event.dart'; +import 'package:fluffychat/pangea/models/representation_content_model.dart'; import 'package:fluffychat/pangea/models/speech_to_text_models.dart'; +import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/utils/bot_name.dart'; import 'package:fluffychat/pangea/widgets/chat/message_audio_card.dart'; import 'package:flutter/material.dart'; @@ -270,36 +271,33 @@ class PangeaMessageEvent { null; }).toSet(); - Set get transcriptionEvents => _event.aggregatedEvents( - timeline, - PangeaEventTypes.transcript, - ); - - Event? get transcriptionEvent => transcriptionEvents.lastOrNull; - - Future getSpeechToTextLocal() async { - if (transcriptionEvent == null) return null; - - return SpeechToTextResponseModel.fromJson( - transcriptionEvent!.content[PangeaEventTypes.transcript] - as Map, - ); - } - - Future getSpeechToTextGlobal( + Future getSpeechToText( String l1Code, String l2Code, ) async { if (!isAudioMessage) { ErrorHandler.logError( - e: 'Message is not an audio message ${_event.eventId}', + e: 'Calling getSpeechToText on non-audio message', s: StackTrace.current, - data: _event.content, + data: { + "content": _event.content, + "eventId": _event.eventId, + "roomId": _event.roomId, + "userId": _event.room.client.userID, + "account_data": _event.room.client.accountData, + }, ); return null; } - if (transcriptionEvent != null) return getSpeechToTextLocal(); + final SpeechToTextModel? speechToTextLocal = representations + .firstWhereOrNull( + (element) => element.content.speechToText != null, + ) + ?.content + .speechToText; + + if (speechToTextLocal != null) return speechToTextLocal; final matrixFile = await _event.downloadAndDecryptAttachment(); // Pangea# @@ -323,7 +321,10 @@ class PangeaMessageEvent { // audioFile = file; - final SpeechToTextResponseModel response = + debugPrint("mimeType ${matrixFile.mimeType}"); + debugPrint("encoding ${mimeTypeToAudioEncoding(matrixFile.mimeType)}"); + + final SpeechToTextModel response = await MatrixState.pangeaController.speechToText.get( SpeechToTextRequestModel( audioContent: matrixFile.bytes, @@ -332,7 +333,7 @@ class PangeaMessageEvent { 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: 44100, + sampleRateHertz: 22050, userL1: l1Code, userL2: l2Code, ), diff --git a/lib/pangea/models/pangea_representation_event.dart b/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart similarity index 86% rename from lib/pangea/models/pangea_representation_event.dart rename to lib/pangea/matrix_event_wrappers/pangea_representation_event.dart index b79bdd014..88cab3203 100644 --- a/lib/pangea/models/pangea_representation_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart @@ -1,8 +1,10 @@ import 'dart:developer'; import 'package:fluffychat/pangea/extensions/pangea_event_extension.dart'; -import 'package:fluffychat/pangea/models/pangea_choreo_event.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_choreo_event.dart'; import 'package:fluffychat/pangea/models/pangea_token_model.dart'; +import 'package:fluffychat/pangea/models/speech_to_text_models.dart'; +import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/repo/tokens_repo.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -13,9 +15,9 @@ import 'package:sentry_flutter/sentry_flutter.dart'; import '../../widgets/matrix.dart'; import '../constants/language_keys.dart'; import '../constants/pangea_event_types.dart'; +import '../models/choreo_record.dart'; +import '../models/representation_content_model.dart'; import '../utils/error_handler.dart'; -import 'choreo_record.dart'; -import 'message_data_models.dart'; import 'pangea_tokens_event.dart'; class RepresentationEvent { @@ -25,12 +27,15 @@ class RepresentationEvent { ChoreoRecord? _choreo; Timeline timeline; + SpeechToTextModel? _speechToTextResponse; + RepresentationEvent({ required this.timeline, Event? event, PangeaRepresentation? content, PangeaMessageTokens? tokens, ChoreoRecord? choreo, + SpeechToTextModel? speechToTextResponse, }) { if (event != null && event.type != PangeaEventTypes.representation) { throw Exception( @@ -41,10 +46,14 @@ class RepresentationEvent { _content = content; _tokens = tokens; _choreo = choreo; + _speechToTextResponse = speechToTextResponse; } Event? get event => _event; + // 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] PangeaRepresentation get content { if (_content != null) return _content!; _content = _event?.getPangeaContent(); diff --git a/lib/pangea/models/pangea_tokens_event.dart b/lib/pangea/matrix_event_wrappers/pangea_tokens_event.dart similarity index 91% rename from lib/pangea/models/pangea_tokens_event.dart rename to lib/pangea/matrix_event_wrappers/pangea_tokens_event.dart index 244455766..0c138c637 100644 --- a/lib/pangea/models/pangea_tokens_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_tokens_event.dart @@ -1,9 +1,9 @@ +import 'package:fluffychat/pangea/extensions/pangea_event_extension.dart'; +import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; +import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/pangea/extensions/pangea_event_extension.dart'; -import 'package:fluffychat/pangea/utils/error_handler.dart'; import '../constants/pangea_event_types.dart'; -import 'message_data_models.dart'; class TokensEvent { Event event; diff --git a/lib/pangea/models/message_data_models.dart b/lib/pangea/models/representation_content_model.dart similarity index 66% rename from lib/pangea/models/message_data_models.dart rename to lib/pangea/models/representation_content_model.dart index 1ed2f36ce..dc81f191e 100644 --- a/lib/pangea/models/message_data_models.dart +++ b/lib/pangea/models/representation_content_model.dart @@ -1,9 +1,8 @@ -import 'dart:convert'; - +import 'package:fluffychat/pangea/constants/language_keys.dart'; +import 'package:fluffychat/pangea/models/speech_to_text_models.dart'; +import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/pangea/models/pangea_token_model.dart'; - /// this class is contained within a [RepresentationEvent] /// this event is the child of a [EventTypes.Message] /// the event has two potential children events - @@ -22,6 +21,9 @@ class PangeaRepresentation { bool originalSent; bool originalWritten; + // a representation can be create via speech to text on the original message + SpeechToTextModel? speechToText; + // how do we know which representation was sent by author? // RepresentationEvent.text == PangeaMessageEvent.event.body // use: to know whether directUse @@ -49,20 +51,33 @@ class PangeaRepresentation { required this.text, required this.originalSent, required this.originalWritten, + this.speechToText, }); - factory PangeaRepresentation.fromJson(Map json) => - PangeaRepresentation( - langCode: json[_langCodeKey], - text: json[_textKey], - originalSent: json[_originalSentKey] ?? false, - originalWritten: json[_originalWrittenKey] ?? false, + factory PangeaRepresentation.fromJson(Map json) { + if (json[_langCodeKey] == LanguageKeys.unknownLanguage) { + ErrorHandler.logError( + e: Exception("Language code cannot be 'unk'"), + s: StackTrace.current, + data: {"rep_content": json}, ); + } + return PangeaRepresentation( + langCode: json[_langCodeKey], + text: json[_textKey], + originalSent: json[_originalSentKey] ?? false, + originalWritten: json[_originalWrittenKey] ?? false, + speechToText: json[_speechToTextKey] == null + ? null + : SpeechToTextModel.fromJson(json[_speechToTextKey]), + ); + } static const _textKey = "txt"; static const _langCodeKey = "lang"; static const _originalSentKey = "snt"; static const _originalWrittenKey = "wrttn"; + static const _speechToTextKey = "stt"; Map toJson() { final data = {}; @@ -70,35 +85,9 @@ class PangeaRepresentation { data[_langCodeKey] = langCode; if (originalSent) data[_originalSentKey] = originalSent; if (originalWritten) data[_originalWrittenKey] = originalWritten; - return data; - } -} - -/// this class lives within a [PangeaTokensEvent] -/// it always has a [RepresentationEvent] parent -/// These live as separate event so that anyone can add and edit tokens to -/// representation -class PangeaMessageTokens { - List tokens; - - PangeaMessageTokens({ - required this.tokens, - }); - - factory PangeaMessageTokens.fromJson(Map json) { - return PangeaMessageTokens( - tokens: (jsonDecode(json[_tokensKey] ?? "[]") as Iterable) - .map((e) => PangeaToken.fromJson(e)) - .toList() - .cast(), - ); - } - - static const _tokensKey = "tkns"; - - Map toJson() { - final data = {}; - data[_tokensKey] = jsonEncode(tokens.map((e) => e.toJson()).toList()); + if (speechToText != null) { + data[_speechToTextKey] = speechToText!.toJson(); + } return data; } } diff --git a/lib/pangea/models/speech_to_text_models.dart b/lib/pangea/models/speech_to_text_models.dart index 83a04ec8b..18cf95825 100644 --- a/lib/pangea/models/speech_to_text_models.dart +++ b/lib/pangea/models/speech_to_text_models.dart @@ -1,7 +1,10 @@ import 'dart:convert'; +import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/enum/audio_encoding_enum.dart'; +import 'package:fluffychat/pangea/models/pangea_token_model.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; class SpeechToTextAudioConfigModel { @@ -67,21 +70,37 @@ class SpeechToTextRequestModel { } } -class WordInfo { - final String word; +class STTToken { + final PangeaToken token; final Duration? startTime; final Duration? endTime; final int? confidence; - WordInfo({ - required this.word, + STTToken({ + required this.token, this.startTime, this.endTime, this.confidence, }); - factory WordInfo.fromJson(Map json) => WordInfo( - word: json['word'], + int get offset => token.text.offset; + + int get length => token.text.length; + + Color color(BuildContext context) { + if (confidence == null || confidence! > 80) { + return Theme.of(context).brightness == Brightness.dark + ? AppConfig.primaryColorLight + : AppConfig.primaryColor; + } + if (confidence! > 50) { + return const Color.fromARGB(255, 184, 142, 43); + } + return Colors.red; + } + + factory STTToken.fromJson(Map json) => STTToken( + token: PangeaToken.fromJson(json['token']), startTime: json['start_time'] != null ? Duration(milliseconds: json['start_time']) : null, @@ -92,7 +111,7 @@ class WordInfo { ); Map toJson() => { - "word": word, + "token": token, "start_time": startTime?.inMilliseconds, "end_time": endTime?.inMilliseconds, "confidence": confidence, @@ -100,27 +119,32 @@ class WordInfo { } class Transcript { - final String transcript; + final String text; final int confidence; - final List words; + final List sttTokens; + final String langCode; Transcript({ - required this.transcript, + required this.text, required this.confidence, - required this.words, + required this.sttTokens, + required this.langCode, }); factory Transcript.fromJson(Map json) => Transcript( - transcript: json['transcript'], + text: json['transcript'], confidence: json['confidence'].toDouble(), - words: - (json['words'] as List).map((e) => WordInfo.fromJson(e)).toList(), + sttTokens: (json['stt_tokens'] as List) + .map((e) => STTToken.fromJson(e)) + .toList(), + langCode: json['lang_code'], ); Map toJson() => { - "transcript": transcript, + "transcript": text, "confidence": confidence, - "words": words.map((e) => e.toJson()).toList(), + "stt_tokens": sttTokens.map((e) => e.toJson()).toList(), + "lang_code": langCode, }; } @@ -141,15 +165,19 @@ class SpeechToTextResult { }; } -class SpeechToTextResponseModel { +class SpeechToTextModel { final List results; - SpeechToTextResponseModel({ + SpeechToTextModel({ required this.results, }); - factory SpeechToTextResponseModel.fromJson(Map json) => - SpeechToTextResponseModel( + Transcript get transcript => results.first.transcripts.first; + + String get langCode => results.first.transcripts.first.langCode; + + factory SpeechToTextModel.fromJson(Map json) => + SpeechToTextModel( results: (json['results'] as List) .map((e) => SpeechToTextResult.fromJson(e)) .toList(), diff --git a/lib/pangea/models/tokens_event_content_model.dart b/lib/pangea/models/tokens_event_content_model.dart new file mode 100644 index 000000000..f2a7db7a6 --- /dev/null +++ b/lib/pangea/models/tokens_event_content_model.dart @@ -0,0 +1,32 @@ +import 'dart:convert'; + +import 'package:fluffychat/pangea/models/pangea_token_model.dart'; + +/// this class lives within a [PangeaTokensEvent] +/// it always has a [RepresentationEvent] parent +/// These live as separate event so that anyone can add and edit tokens to +/// representation +class PangeaMessageTokens { + List tokens; + + PangeaMessageTokens({ + required this.tokens, + }); + + factory PangeaMessageTokens.fromJson(Map json) { + return PangeaMessageTokens( + tokens: (jsonDecode(json[_tokensKey] ?? "[]") as Iterable) + .map((e) => PangeaToken.fromJson(e)) + .toList() + .cast(), + ); + } + + static const _tokensKey = "tkns"; + + Map toJson() { + final data = {}; + data[_tokensKey] = jsonEncode(tokens.map((e) => e.toJson()).toList()); + return data; + } +} diff --git a/lib/pangea/pages/analytics/construct_list.dart b/lib/pangea/pages/analytics/construct_list.dart index d81fe2b38..dd6c26618 100644 --- a/lib/pangea/pages/analytics/construct_list.dart +++ b/lib/pangea/pages/analytics/construct_list.dart @@ -5,11 +5,11 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/enum/construct_type_enum.dart'; -import 'package:fluffychat/pangea/models/construct_analytics_event.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/construct_analytics_event.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_representation_event.dart'; import 'package:fluffychat/pangea/models/constructs_analytics_model.dart'; import 'package:fluffychat/pangea/models/pangea_match_model.dart'; -import 'package:fluffychat/pangea/models/pangea_message_event.dart'; -import 'package:fluffychat/pangea/models/pangea_representation_event.dart'; import 'package:fluffychat/pangea/pages/analytics/base_analytics.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/string_color.dart'; diff --git a/lib/pangea/utils/bot_style.dart b/lib/pangea/utils/bot_style.dart index 8c971b307..3791c205e 100644 --- a/lib/pangea/utils/bot_style.dart +++ b/lib/pangea/utils/bot_style.dart @@ -1,7 +1,6 @@ -import 'package:flutter/material.dart'; - import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:flutter/material.dart'; class BotStyle { static TextStyle text( @@ -25,6 +24,7 @@ class BotStyle { ? AppConfig.primaryColorLight : AppConfig.primaryColor : null, + inherit: true, ); return existingStyle?.merge(botStyle) ?? botStyle; diff --git a/lib/pangea/utils/download_chat.dart b/lib/pangea/utils/download_chat.dart index 9e3d4d423..a73cfa129 100644 --- a/lib/pangea/utils/download_chat.dart +++ b/lib/pangea/utils/download_chat.dart @@ -16,7 +16,7 @@ import 'package:syncfusion_flutter_xlsio/xlsio.dart'; import 'package:universal_html/html.dart' as webFile; import 'package:fluffychat/pangea/models/class_model.dart'; -import 'package:fluffychat/pangea/models/pangea_message_event.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import '../models/choreo_record.dart'; diff --git a/lib/pangea/utils/get_chat_list_item_subtitle.dart b/lib/pangea/utils/get_chat_list_item_subtitle.dart index cddf31435..b6c894e97 100644 --- a/lib/pangea/utils/get_chat_list_item_subtitle.dart +++ b/lib/pangea/utils/get_chat_list_item_subtitle.dart @@ -1,8 +1,8 @@ import 'package:fluffychat/pangea/constants/language_keys.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/class_model.dart'; -import 'package:fluffychat/pangea/models/pangea_message_event.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; diff --git a/lib/pangea/widgets/chat/message_audio_card.dart b/lib/pangea/widgets/chat/message_audio_card.dart index 71e3fa629..5c1f8e67b 100644 --- a/lib/pangea/widgets/chat/message_audio_card.dart +++ b/lib/pangea/widgets/chat/message_audio_card.dart @@ -1,6 +1,7 @@ import 'package:fluffychat/pages/chat/events/audio_player.dart'; -import 'package:fluffychat/pangea/models/pangea_message_event.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:fluffychat/pangea/widgets/chat/toolbar_content_loading_indicator.dart'; import 'package:fluffychat/pangea/widgets/igc/card_error_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -74,17 +75,9 @@ class MessageAudioCardState extends State { @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8), + return Container( child: _isLoading - ? SizedBox( - height: 14, - width: 14, - child: CircularProgressIndicator( - strokeWidth: 2.0, - color: Theme.of(context).colorScheme.primary, - ), - ) + ? const ToolbarContentLoadingIndicator() : localAudioEvent != null || audioFile != null ? Container( constraints: const BoxConstraints( diff --git a/lib/pangea/widgets/chat/message_speech_to_text_card.dart b/lib/pangea/widgets/chat/message_speech_to_text_card.dart index 3a717349c..5118eba96 100644 --- a/lib/pangea/widgets/chat/message_speech_to_text_card.dart +++ b/lib/pangea/widgets/chat/message_speech_to_text_card.dart @@ -1,7 +1,9 @@ -import 'package:fluffychat/pangea/models/pangea_message_event.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/speech_to_text_models.dart'; -import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:fluffychat/pangea/widgets/chat/speech_to_text_score.dart'; +import 'package:fluffychat/pangea/widgets/chat/speech_to_text_text.dart'; +import 'package:fluffychat/pangea/widgets/chat/toolbar_content_loading_indicator.dart'; import 'package:fluffychat/pangea/widgets/igc/card_error_widget.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; @@ -19,7 +21,7 @@ class MessageSpeechToTextCard extends StatefulWidget { } class MessageSpeechToTextCardState extends State { - SpeechToTextResponseModel? speechToTextResponse; + SpeechToTextModel? speechToTextResponse; bool _fetchingTranscription = true; Object? error; @@ -32,9 +34,6 @@ class MessageSpeechToTextCardState extends State { roomID: widget.messageEvent.room.id, ); - String? get transcription => speechToTextResponse - ?.results.firstOrNull?.transcripts.firstOrNull?.transcript; - // look for transcription in message event // if not found, call API to transcribe audio Future getSpeechToText() async { @@ -43,7 +42,11 @@ class MessageSpeechToTextCardState extends State { throw Exception('Language selection not found'); } speechToTextResponse ??= - await widget.messageEvent.getSpeechToTextGlobal(l1Code!, l2Code!); + await widget.messageEvent.getSpeechToText(l1Code!, l2Code!); + + debugPrint( + 'Speech to text transcript: ${speechToTextResponse?.transcript.text}', + ); } catch (e, s) { error = e; ErrorHandler.logError( @@ -64,25 +67,23 @@ class MessageSpeechToTextCardState extends State { @override Widget build(BuildContext context) { - if (!_fetchingTranscription && speechToTextResponse == null) { + if (_fetchingTranscription) { + return const ToolbarContentLoadingIndicator(); + } + + //done fetchig but not results means some kind of error + if (speechToTextResponse == null) { return CardErrorWidget(error: error); } - return Padding( - padding: const EdgeInsets.all(8), - child: _fetchingTranscription - ? SizedBox( - height: 14, - width: 14, - child: CircularProgressIndicator( - strokeWidth: 2.0, - color: Theme.of(context).colorScheme.primary, - ), - ) - : Text( - transcription!, - style: BotStyle.text(context), - ), + return Column( + children: [ + SpeechToTextText(transcript: speechToTextResponse!.transcript), + const Divider(), + SpeechToTextScoreWidget( + score: speechToTextResponse!.transcript.confidence, + ), + ], ); } } diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 196416422..84664f3c1 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -5,7 +5,7 @@ import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pangea/constants/local.key.dart'; import 'package:fluffychat/pangea/enum/message_mode_enum.dart'; -import 'package:fluffychat/pangea/models/pangea_message_event.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/utils/any_state_holder.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/overlay.dart'; @@ -264,9 +264,11 @@ class MessageToolbarState extends State { ) ?? true; autoplay - ? updateMode(widget.pangeaMessageEvent.isAudioMessage - ? MessageMode.speechToText - : MessageMode.textToSpeech) + ? updateMode( + widget.pangeaMessageEvent.isAudioMessage + ? MessageMode.speechToText + : MessageMode.textToSpeech, + ) : updateMode(MessageMode.translation); }); @@ -319,7 +321,10 @@ class MessageToolbarState extends State { duration: FluffyThemes.animationDuration, child: Column( children: [ - toolbarContent ?? const SizedBox(), + Padding( + padding: const EdgeInsets.all(8.0), + child: toolbarContent ?? const SizedBox(), + ), SizedBox(height: toolbarContent == null ? 0 : 20), ], ), @@ -329,7 +334,7 @@ class MessageToolbarState extends State { Row( mainAxisSize: MainAxisSize.min, children: MessageMode.values.map((mode) { - if ([MessageMode.definition, MessageMode.textToSpeech] + if ([MessageMode.definition, MessageMode.textToSpeech, MessageMode.translation] .contains(mode) && widget.pangeaMessageEvent.isAudioMessage) { return const SizedBox.shrink(); diff --git a/lib/pangea/widgets/chat/message_translation_card.dart b/lib/pangea/widgets/chat/message_translation_card.dart index 5014d83f3..780f12f8a 100644 --- a/lib/pangea/widgets/chat/message_translation_card.dart +++ b/lib/pangea/widgets/chat/message_translation_card.dart @@ -1,9 +1,10 @@ -import 'package:fluffychat/pangea/models/message_data_models.dart'; -import 'package:fluffychat/pangea/models/pangea_message_event.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; +import 'package:fluffychat/pangea/models/representation_content_model.dart'; import 'package:fluffychat/pangea/repo/full_text_translation_repo.dart'; import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/widgets/chat/message_text_selection.dart'; +import 'package:fluffychat/pangea/widgets/chat/toolbar_content_loading_indicator.dart'; import 'package:fluffychat/pangea/widgets/igc/card_error_widget.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; @@ -139,17 +140,9 @@ class MessageTranslationCardState extends State { return const CardErrorWidget(); } - return Padding( - padding: const EdgeInsets.all(8), + return Container( child: _fetchingRepresentation - ? SizedBox( - height: 14, - width: 14, - child: CircularProgressIndicator( - strokeWidth: 2.0, - color: Theme.of(context).colorScheme.primary, - ), - ) + ? const ToolbarContentLoadingIndicator() : selectionTranslation != null ? Text( selectionTranslation!, diff --git a/lib/pangea/widgets/chat/message_unsubscribed_card.dart b/lib/pangea/widgets/chat/message_unsubscribed_card.dart index c0574aa6f..5b20fc403 100644 --- a/lib/pangea/widgets/chat/message_unsubscribed_card.dart +++ b/lib/pangea/widgets/chat/message_unsubscribed_card.dart @@ -37,34 +37,31 @@ class MessageUnsubscribedCard extends StatelessWidget { MatrixState.pAnyState.closeOverlay(); } - return Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 10, 0), - child: Column( - children: [ - Text( - style: BotStyle.text(context), - "${L10n.of(context)!.subscribedToUnlockTools} $languageTool", - textAlign: TextAlign.center, - ), - const SizedBox(height: 10), - SizedBox( - width: double.infinity, - child: TextButton( - onPressed: onButtonPress, - style: ButtonStyle( - backgroundColor: MaterialStateProperty.all( - (AppConfig.primaryColor).withOpacity(0.1), - ), - ), - child: Text( - inTrialWindow - ? L10n.of(context)!.activateTrial - : L10n.of(context)!.getAccess, + return Column( + children: [ + Text( + style: BotStyle.text(context), + "${L10n.of(context)!.subscribedToUnlockTools} $languageTool", + textAlign: TextAlign.center, + ), + const SizedBox(height: 10), + SizedBox( + width: double.infinity, + child: TextButton( + onPressed: onButtonPress, + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all( + (AppConfig.primaryColor).withOpacity(0.1), ), ), + child: Text( + inTrialWindow + ? L10n.of(context)!.activateTrial + : L10n.of(context)!.getAccess, + ), ), - ], - ), + ), + ], ); } } diff --git a/lib/pangea/widgets/chat/overlay_message.dart b/lib/pangea/widgets/chat/overlay_message.dart index e39ffe827..f0d927b0b 100644 --- a/lib/pangea/widgets/chat/overlay_message.dart +++ b/lib/pangea/widgets/chat/overlay_message.dart @@ -1,7 +1,7 @@ import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat/events/message_content.dart'; import 'package:fluffychat/pangea/enum/use_type.dart'; -import 'package:fluffychat/pangea/models/pangea_message_event.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:flutter/material.dart'; diff --git a/lib/pangea/widgets/chat/speech_to_text_score.dart b/lib/pangea/widgets/chat/speech_to_text_score.dart new file mode 100644 index 000000000..1e45f73b2 --- /dev/null +++ b/lib/pangea/widgets/chat/speech_to_text_score.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +class SpeechToTextScoreWidget extends StatefulWidget { + final int score; + const SpeechToTextScoreWidget({super.key, required this.score}); + + @override + SpeechToTextScoreWidgetState createState() => SpeechToTextScoreWidgetState(); +} + +class SpeechToTextScoreWidgetState extends State { + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 500), + transitionBuilder: (Widget child, Animation animation) { + return FadeTransition(opacity: animation, child: child); + }, + child: Text( + 'Score: ${widget.score}', + key: ValueKey(widget.score), + style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + ), + ), + ); + } +} diff --git a/lib/pangea/widgets/chat/speech_to_text_text.dart b/lib/pangea/widgets/chat/speech_to_text_text.dart new file mode 100644 index 000000000..56b436ee6 --- /dev/null +++ b/lib/pangea/widgets/chat/speech_to_text_text.dart @@ -0,0 +1,65 @@ +import 'package:fluffychat/pangea/models/speech_to_text_models.dart'; +import 'package:fluffychat/pangea/utils/bot_style.dart'; +import 'package:flutter/material.dart'; + +class SpeechToTextText extends StatelessWidget { + final Transcript transcript; + + const SpeechToTextText({super.key, required this.transcript}); + + @override + Widget build(BuildContext context) { + return RichText( + text: _buildTranscriptText(context, transcript), + ); + } + + TextSpan _buildTranscriptText(BuildContext context, Transcript transcript) { + final List spans = []; + final String fullText = transcript.text; + int lastEnd = 0; + + for (final token in transcript.sttTokens) { + // debugPrint('Token confidence: ${token.confidence}'); + // debugPrint('color: ${token.color(context)}'); + if (token.offset > lastEnd) { + // Add any plain text before the token + spans.add( + TextSpan( + text: fullText.substring(lastEnd, token.offset), + ), + ); + // debugPrint('Pre: ${fullText.substring(lastEnd, token.offset)}'); + } + + spans.add( + TextSpan( + text: fullText.substring(token.offset, token.offset + token.length), + style: BotStyle.text( + context, + existingStyle: TextStyle(color: token.color(context)), + setColor: false, + ), + ), + ); + + // debugPrint( + // 'Main: ${fullText.substring(token.offset, token.offset + token.length)}', + // ); + + lastEnd = token.offset + token.length; + } + + if (lastEnd < fullText.length) { + // Add any remaining text after the last token + spans.add( + TextSpan( + text: fullText.substring(lastEnd), + ), + ); + // debugPrint('Post: ${fullText.substring(lastEnd)}'); + } + + return TextSpan(children: spans); + } +} diff --git a/lib/pangea/widgets/chat/text_to_speech_button.dart b/lib/pangea/widgets/chat/text_to_speech_button.dart index 5facb684b..6e9e8e443 100644 --- a/lib/pangea/widgets/chat/text_to_speech_button.dart +++ b/lib/pangea/widgets/chat/text_to_speech_button.dart @@ -3,7 +3,7 @@ import 'dart:developer'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/events/audio_player.dart'; -import 'package:fluffychat/pangea/models/pangea_message_event.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; diff --git a/lib/pangea/widgets/chat/toolbar_content_loading_indicator.dart b/lib/pangea/widgets/chat/toolbar_content_loading_indicator.dart new file mode 100644 index 000000000..021f4ee44 --- /dev/null +++ b/lib/pangea/widgets/chat/toolbar_content_loading_indicator.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class ToolbarContentLoadingIndicator extends StatelessWidget { + const ToolbarContentLoadingIndicator({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 14, + width: 14, + child: CircularProgressIndicator( + strokeWidth: 2.0, + color: Theme.of(context).colorScheme.primary, + ), + ); + } +} \ No newline at end of file diff --git a/lib/pangea/widgets/igc/pangea_rich_text.dart b/lib/pangea/widgets/igc/pangea_rich_text.dart index 612fabe8d..355b308d5 100644 --- a/lib/pangea/widgets/igc/pangea_rich_text.dart +++ b/lib/pangea/widgets/igc/pangea_rich_text.dart @@ -4,8 +4,8 @@ import 'dart:ui'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/constants/language_keys.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; -import 'package:fluffychat/pangea/models/message_data_models.dart'; -import 'package:fluffychat/pangea/models/pangea_message_event.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; +import 'package:fluffychat/pangea/models/representation_content_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/instructions.dart'; import 'package:fluffychat/pangea/widgets/chat/message_context_menu.dart'; @@ -87,8 +87,10 @@ class PangeaRichTextState extends State { context: context, langCode: widget.pangeaMessageEvent.messageDisplayLangCode, ) - .onError((error, stackTrace) => - ErrorHandler.logError(e: error, s: stackTrace)) + .onError( + (error, stackTrace) => + ErrorHandler.logError(e: error, s: stackTrace), + ) .then((event) { repEvent = event; widget.toolbarController?.toolbar?.textSelection.setMessageText( diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 221a21755..4c892509b 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -3,7 +3,10 @@ PODS: - FlutterMacOS - audio_session (0.0.1): - FlutterMacOS - - desktop_drop (0.0.1): + - connectivity_plus (0.0.1): + - FlutterMacOS + - ReachabilitySwift + - desktop_lifecycle (0.0.1): - FlutterMacOS - device_info_plus (0.0.1): - FlutterMacOS @@ -13,6 +16,66 @@ PODS: - FlutterMacOS - file_selector_macos (0.0.1): - FlutterMacOS + - Firebase/Analytics (10.18.0): + - Firebase/Core + - Firebase/Core (10.18.0): + - Firebase/CoreOnly + - FirebaseAnalytics (~> 10.18.0) + - Firebase/CoreOnly (10.18.0): + - FirebaseCore (= 10.18.0) + - Firebase/Messaging (10.18.0): + - Firebase/CoreOnly + - FirebaseMessaging (~> 10.18.0) + - firebase_analytics (10.8.0): + - Firebase/Analytics (= 10.18.0) + - firebase_core + - FlutterMacOS + - firebase_core (2.24.2): + - Firebase/CoreOnly (~> 10.18.0) + - FlutterMacOS + - firebase_messaging (14.7.10): + - Firebase/CoreOnly (~> 10.18.0) + - Firebase/Messaging (~> 10.18.0) + - firebase_core + - FlutterMacOS + - FirebaseAnalytics (10.18.0): + - FirebaseAnalytics/AdIdSupport (= 10.18.0) + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - FirebaseAnalytics/AdIdSupport (10.18.0): + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleAppMeasurement (= 10.18.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - FirebaseCore (10.18.0): + - FirebaseCoreInternal (~> 10.0) + - GoogleUtilities/Environment (~> 7.12) + - GoogleUtilities/Logger (~> 7.12) + - FirebaseCoreInternal (10.21.0): + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - FirebaseInstallations (10.21.0): + - FirebaseCore (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/UserDefaults (~> 7.8) + - PromisesObjC (~> 2.1) + - FirebaseMessaging (10.18.0): + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleDataTransport (~> 9.2) + - GoogleUtilities/AppDelegateSwizzler (~> 7.8) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/Reachability (~> 7.8) + - GoogleUtilities/UserDefaults (~> 7.8) + - nanopb (< 2.30910.0, >= 2.30908.0) - flutter_app_badger (1.3.0): - FlutterMacOS - flutter_local_notifications (0.0.1): @@ -32,12 +95,63 @@ PODS: - FMDB/standard (2.7.5) - geolocator_apple (1.2.0): - FlutterMacOS + - GoogleAppMeasurement (10.18.0): + - GoogleAppMeasurement/AdIdSupport (= 10.18.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - GoogleAppMeasurement/AdIdSupport (10.18.0): + - GoogleAppMeasurement/WithoutAdIdSupport (= 10.18.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - GoogleAppMeasurement/WithoutAdIdSupport (10.18.0): + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - GoogleDataTransport (9.3.0): + - GoogleUtilities/Environment (~> 7.7) + - nanopb (< 2.30910.0, >= 2.30908.0) + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/AppDelegateSwizzler (7.12.0): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Environment (7.12.0): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.12.0): + - GoogleUtilities/Environment + - GoogleUtilities/MethodSwizzler (7.12.0): + - GoogleUtilities/Logger + - GoogleUtilities/Network (7.12.0): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (7.12.0)" + - GoogleUtilities/Reachability (7.12.0): + - GoogleUtilities/Logger + - GoogleUtilities/UserDefaults (7.12.0): + - GoogleUtilities/Logger + - in_app_purchase_storekit (0.0.1): + - Flutter + - FlutterMacOS - just_audio (0.0.1): - FlutterMacOS - macos_ui (0.1.0): - FlutterMacOS - macos_window_utils (1.0.0): - FlutterMacOS + - nanopb (2.30909.1): + - nanopb/decode (= 2.30909.1) + - nanopb/encode (= 2.30909.1) + - nanopb/decode (2.30909.1) + - nanopb/encode (2.30909.1) - package_info_plus (0.0.1): - FlutterMacOS - pasteboard (0.0.1): @@ -45,8 +159,24 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - - record_macos (0.2.0): + - PromisesObjC (2.3.1) + - purchases_flutter (5.8.0): - FlutterMacOS + - PurchasesHybridCommon (= 6.3.0) + - PurchasesHybridCommon (6.3.0): + - RevenueCat (= 4.26.1) + - ReachabilitySwift (5.0.0) + - record_darwin (1.0.0): + - Flutter + - FlutterMacOS + - RevenueCat (4.26.1) + - Sentry/HybridSDK (8.17.2): + - SentryPrivate (= 8.17.2) + - sentry_flutter (0.0.1): + - Flutter + - FlutterMacOS + - Sentry/HybridSDK (= 8.17.2) + - SentryPrivate (8.17.2) - share_plus (0.0.1): - FlutterMacOS - shared_preferences_foundation (0.0.1): @@ -80,11 +210,15 @@ PODS: DEPENDENCIES: - appkit_ui_element_colors (from `Flutter/ephemeral/.symlinks/plugins/appkit_ui_element_colors/macos`) - audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`) - - desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`) + - connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`) + - desktop_lifecycle (from `Flutter/ephemeral/.symlinks/plugins/desktop_lifecycle/macos`) - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - dynamic_color (from `Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos`) - emoji_picker_flutter (from `Flutter/ephemeral/.symlinks/plugins/emoji_picker_flutter/macos`) - file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`) + - firebase_analytics (from `Flutter/ephemeral/.symlinks/plugins/firebase_analytics/macos`) + - firebase_core (from `Flutter/ephemeral/.symlinks/plugins/firebase_core/macos`) + - firebase_messaging (from `Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos`) - flutter_app_badger (from `Flutter/ephemeral/.symlinks/plugins/flutter_app_badger/macos`) - flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`) - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) @@ -92,13 +226,16 @@ DEPENDENCIES: - flutter_webrtc (from `Flutter/ephemeral/.symlinks/plugins/flutter_webrtc/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - geolocator_apple (from `Flutter/ephemeral/.symlinks/plugins/geolocator_apple/macos`) + - in_app_purchase_storekit (from `Flutter/ephemeral/.symlinks/plugins/in_app_purchase_storekit/darwin`) - just_audio (from `Flutter/ephemeral/.symlinks/plugins/just_audio/macos`) - macos_ui (from `Flutter/ephemeral/.symlinks/plugins/macos_ui/macos`) - macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - - record_macos (from `Flutter/ephemeral/.symlinks/plugins/record_macos/macos`) + - purchases_flutter (from `Flutter/ephemeral/.symlinks/plugins/purchases_flutter/macos`) + - record_darwin (from `Flutter/ephemeral/.symlinks/plugins/record_darwin/macos`) + - sentry_flutter (from `Flutter/ephemeral/.symlinks/plugins/sentry_flutter/macos`) - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`) @@ -111,7 +248,23 @@ DEPENDENCIES: SPEC REPOS: trunk: + - Firebase + - FirebaseAnalytics + - FirebaseCore + - FirebaseCoreInternal + - FirebaseInstallations + - FirebaseMessaging - FMDB + - GoogleAppMeasurement + - GoogleDataTransport + - GoogleUtilities + - nanopb + - PromisesObjC + - PurchasesHybridCommon + - ReachabilitySwift + - RevenueCat + - Sentry + - SentryPrivate - SQLCipher - WebRTC-SDK @@ -120,8 +273,10 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/appkit_ui_element_colors/macos audio_session: :path: Flutter/ephemeral/.symlinks/plugins/audio_session/macos - desktop_drop: - :path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos + connectivity_plus: + :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos + desktop_lifecycle: + :path: Flutter/ephemeral/.symlinks/plugins/desktop_lifecycle/macos device_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos dynamic_color: @@ -130,6 +285,12 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/emoji_picker_flutter/macos file_selector_macos: :path: Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos + firebase_analytics: + :path: Flutter/ephemeral/.symlinks/plugins/firebase_analytics/macos + firebase_core: + :path: Flutter/ephemeral/.symlinks/plugins/firebase_core/macos + firebase_messaging: + :path: Flutter/ephemeral/.symlinks/plugins/firebase_messaging/macos flutter_app_badger: :path: Flutter/ephemeral/.symlinks/plugins/flutter_app_badger/macos flutter_local_notifications: @@ -144,6 +305,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral geolocator_apple: :path: Flutter/ephemeral/.symlinks/plugins/geolocator_apple/macos + in_app_purchase_storekit: + :path: Flutter/ephemeral/.symlinks/plugins/in_app_purchase_storekit/darwin just_audio: :path: Flutter/ephemeral/.symlinks/plugins/just_audio/macos macos_ui: @@ -156,8 +319,12 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/pasteboard/macos path_provider_foundation: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin - record_macos: - :path: Flutter/ephemeral/.symlinks/plugins/record_macos/macos + purchases_flutter: + :path: Flutter/ephemeral/.symlinks/plugins/purchases_flutter/macos + record_darwin: + :path: Flutter/ephemeral/.symlinks/plugins/record_darwin/macos + sentry_flutter: + :path: Flutter/ephemeral/.symlinks/plugins/sentry_flutter/macos share_plus: :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos shared_preferences_foundation: @@ -180,11 +347,21 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: appkit_ui_element_colors: 39bb2d80be3f19b152ccf4c70d5bbe6cba43d74a audio_session: dea1f41890dbf1718f04a56f1d6150fd50039b72 - desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898 + connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747 + desktop_lifecycle: a600c10e12fe033c7be9078f2e929b8241f2c1e3 device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f dynamic_color: 2eaa27267de1ca20d879fbd6e01259773fb1670f emoji_picker_flutter: 533634326b1c5de9a181ba14b9758e6dfe967a20 file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9 + Firebase: 414ad272f8d02dfbf12662a9d43f4bba9bec2a06 + firebase_analytics: 687a47ef9af9c5a8a9fc612c100987f843d0a281 + firebase_core: a74ee8b3ab5f91ae6b73f4913eaca996c24458b6 + firebase_messaging: 1298099739b30786ab5be9fdbfe00b2019065745 + FirebaseAnalytics: 4d310b35c48eaa4a058ddc04bdca6bdb5dc0fe80 + FirebaseCore: 2322423314d92f946219c8791674d2f3345b598f + FirebaseCoreInternal: 43c1788eaeee9d1b97caaa751af567ce11010d00 + FirebaseInstallations: 390ea1d10a4d02b20c965cbfd527ee9b3b412acb + FirebaseMessaging: 9bc34a98d2e0237e1b121915120d4d48ddcf301e flutter_app_badger: 55a64b179f8438e89d574320c77b306e327a1730 flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4 flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea @@ -193,25 +370,38 @@ SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a geolocator_apple: 821be05bbdb1b49500e029ebcbf2d6acf2dfb966 + GoogleAppMeasurement: 70ce9aa438cff1cfb31ea3e660bcc67734cb716e + GoogleDataTransport: 57c22343ab29bc686febbf7cbb13bad167c2d8fe + GoogleUtilities: 0759d1a57ebb953965c2dfe0ba4c82e95ccc2e34 + in_app_purchase_storekit: 9e9931234f0adcf71ae323f8c83785b96030edf1 just_audio: 9b67ca7b97c61cfc9784ea23cd8cc55eb226d489 macos_ui: 6229a8922cd97bafb7d9636c8eb8dfb0744183ca macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663 + nanopb: d4d75c12cd1316f4a64e3c6963f879ecd4b5e0d5 package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce pasteboard: 9b69dba6fedbb04866be632205d532fe2f6b1d99 - path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 - record_macos: 937889e0f2a7a12b6fc14e97a3678e5a18943de6 + path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c + PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 + purchases_flutter: 36a8c669148173e56f19dfc20df724bc734ab475 + PurchasesHybridCommon: 5ee5e13fe009876850a03f52bb0349b6fa91d976 + ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 + record_darwin: 1f6619f2abac4d1ca91d3eeab038c980d76f1517 + RevenueCat: 4e8899a69fd57180ef166237d1eb670023be05de + Sentry: 64a9f9c3637af913adcf53deced05bbe452d1410 + sentry_flutter: 57912cf425e09398bdf47f38842a1fcb9836f1be + SentryPrivate: 024c6fed507ac39ae98e6d087034160f942920d5 share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 - shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 + shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea sqflite_sqlcipher: d1ac7c60596e4d624d9757e3ec96e9cfafb734d6 SQLCipher: 905b145f65f349f26da9e60a19901ad24adcd381 url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 video_compress: c896234f100791b5fef7f049afa38f6d2ef7b42f - video_player_avfoundation: e9e6f9cae7d7a6d9b43519b0aab382bca60fcfd1 + video_player_avfoundation: 02011213dab73ae3687df27ce441fbbcc82b5579 wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269 WebRTC-SDK: c24d2a6c9f571f2ed42297cb8ffba9557093142b window_to_front: 4cdc24ddd8461ad1a55fa06286d6a79d8b29e8d8 PODFILE CHECKSUM: d0975b16fbdecb73b109d8fbc88aa77ffe4c7a8d -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.2 diff --git a/needed-translations.txt b/needed-translations.txt index 51e52cf0f..6bc80ea7f 100644 --- a/needed-translations.txt +++ b/needed-translations.txt @@ -829,7 +829,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -1683,7 +1683,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -2537,7 +2537,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -3391,7 +3391,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -4245,7 +4245,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -5068,7 +5068,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -5922,7 +5922,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -6776,7 +6776,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -7615,7 +7615,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -8438,7 +8438,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -9292,7 +9292,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -10146,7 +10146,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -11000,7 +11000,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -11854,7 +11854,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -12677,7 +12677,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -13531,7 +13531,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -14385,7 +14385,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -15226,7 +15226,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -16080,7 +16080,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -16934,7 +16934,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -17788,7 +17788,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -18627,7 +18627,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -19481,7 +19481,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -20335,7 +20335,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -21189,7 +21189,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -22043,7 +22043,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -22897,7 +22897,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -23751,7 +23751,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -24605,7 +24605,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -25459,7 +25459,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -26282,7 +26282,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -27136,7 +27136,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -27990,7 +27990,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -28817,7 +28817,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -29671,7 +29671,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -30525,7 +30525,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -31379,7 +31379,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -32233,7 +32233,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -33087,7 +33087,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -33941,7 +33941,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -34780,7 +34780,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -35619,7 +35619,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -36473,7 +36473,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -37312,7 +37312,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", @@ -38166,7 +38166,7 @@ "more", "translationTooltip", "audioTooltip", - "transcriptTooltip", + "speechToTextTooltip", "certifyAge", "kickBotWarning", "joinToView", From 6cfdd349bd286e3deba313da1f9067180ddafc80 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 7 May 2024 11:32:43 -0400 Subject: [PATCH 4/7] update to num channels --- lib/pages/chat/recording_dialog.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/chat/recording_dialog.dart b/lib/pages/chat/recording_dialog.dart index 62a863a68..b3bff4187 100644 --- a/lib/pages/chat/recording_dialog.dart +++ b/lib/pages/chat/recording_dialog.dart @@ -50,6 +50,7 @@ class RecordingDialogState extends State { bitRate: bitRate, samplingRate: samplingRate, encoder: AudioEncoder.wav, + numChannels: 1, ); setState(() => _duration = Duration.zero); _recorderSubscription?.cancel(); From 191fc6962898c14257edf1920c6356a36d93dda7 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Tue, 7 May 2024 16:10:53 -0400 Subject: [PATCH 5/7] now displaying score for pronunciation --- assets/l10n/intl_en.arb | 6 +- lib/pangea/models/speech_to_text_models.dart | 61 +++- .../chat/message_speech_to_text_card.dart | 144 +++++++-- .../widgets/chat/speech_to_text_score.dart | 29 -- .../widgets/common/icon_number_widget.dart | 47 +++ needed-translations.txt | 288 +++++++++++++++--- 6 files changed, 455 insertions(+), 120 deletions(-) delete mode 100644 lib/pangea/widgets/chat/speech_to_text_score.dart create mode 100644 lib/pangea/widgets/common/icon_number_widget.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 47f866b3b..4941b0dfc 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -3935,5 +3935,9 @@ "unread": {} } }, - "messageAnalytics": "Message Analytics" + "messageAnalytics": "Message Analytics", + "words": "Words", + "score": "Score", + "accuracy": "Accuracy", + "points": "Points" } \ No newline at end of file diff --git a/lib/pangea/models/speech_to_text_models.dart b/lib/pangea/models/speech_to_text_models.dart index 18cf95825..05ab6ccbe 100644 --- a/lib/pangea/models/speech_to_text_models.dart +++ b/lib/pangea/models/speech_to_text_models.dart @@ -1,6 +1,5 @@ import 'dart:convert'; -import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/enum/audio_encoding_enum.dart'; import 'package:fluffychat/pangea/models/pangea_token_model.dart'; import 'package:flutter/foundation.dart'; @@ -88,10 +87,14 @@ class STTToken { int get length => token.text.length; Color color(BuildContext context) { - if (confidence == null || confidence! > 80) { - return Theme.of(context).brightness == Brightness.dark - ? AppConfig.primaryColorLight - : AppConfig.primaryColor; + if (confidence == null) { + return Theme.of(context).textTheme.bodyMedium?.color ?? + (Theme.of(context).brightness == Brightness.dark + ? Colors.white + : Colors.black); + } + if (confidence! > 80) { + return const Color.fromARGB(255, 0, 152, 0); } if (confidence! > 50) { return const Color.fromARGB(255, 184, 142, 43); @@ -99,16 +102,19 @@ class STTToken { return Colors.red; } - factory STTToken.fromJson(Map json) => STTToken( - token: PangeaToken.fromJson(json['token']), - startTime: json['start_time'] != null - ? Duration(milliseconds: json['start_time']) - : null, - endTime: json['end_time'] != null - ? Duration(milliseconds: json['end_time']) - : null, - confidence: json['confidence'], - ); + factory STTToken.fromJson(Map json) { + // debugPrint('STTToken.fromJson: $json'); + return STTToken( + token: PangeaToken.fromJson(json['token']), + startTime: json['start_time'] != null + ? Duration(milliseconds: json['start_time'] * 1000.toInt()) + : null, + endTime: json['end_time'] != null + ? Duration(milliseconds: json['end_time'] * 1000.toInt()) + : null, + confidence: json['confidence'], + ); + } Map toJson() => { "token": token, @@ -116,6 +122,27 @@ class STTToken { "end_time": endTime?.inMilliseconds, "confidence": confidence, }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other is! STTToken) return false; + + return token == other.token && + startTime == other.startTime && + endTime == other.endTime && + confidence == other.confidence; + } + + @override + int get hashCode { + return Object.hashAll([ + token.hashCode, + startTime.hashCode, + endTime.hashCode, + confidence.hashCode, + ]); + } } class Transcript { @@ -133,7 +160,9 @@ class Transcript { factory Transcript.fromJson(Map json) => Transcript( text: json['transcript'], - confidence: json['confidence'].toDouble(), + confidence: json['confidence'] <= 100 + ? json['confidence'] + : json['confidence'] / 100, sttTokens: (json['stt_tokens'] as List) .map((e) => STTToken.fromJson(e)) .toList(), diff --git a/lib/pangea/widgets/chat/message_speech_to_text_card.dart b/lib/pangea/widgets/chat/message_speech_to_text_card.dart index 5118eba96..2ec1fa19d 100644 --- a/lib/pangea/widgets/chat/message_speech_to_text_card.dart +++ b/lib/pangea/widgets/chat/message_speech_to_text_card.dart @@ -1,12 +1,14 @@ import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/speech_to_text_models.dart'; -import 'package:fluffychat/pangea/utils/error_handler.dart'; -import 'package:fluffychat/pangea/widgets/chat/speech_to_text_score.dart'; -import 'package:fluffychat/pangea/widgets/chat/speech_to_text_text.dart'; import 'package:fluffychat/pangea/widgets/chat/toolbar_content_loading_indicator.dart'; +import 'package:fluffychat/pangea/widgets/common/icon_number_widget.dart'; import 'package:fluffychat/pangea/widgets/igc/card_error_widget.dart'; import 'package:fluffychat/widgets/matrix.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +import '../../utils/bot_style.dart'; class MessageSpeechToTextCard extends StatefulWidget { final PangeaMessageEvent messageEvent; @@ -24,6 +26,7 @@ class MessageSpeechToTextCardState extends State { SpeechToTextModel? speechToTextResponse; bool _fetchingTranscription = true; Object? error; + STTToken? selectedToken; String? get l1Code => MatrixState.pangeaController.languageController.activeL1Code( @@ -37,26 +40,90 @@ class MessageSpeechToTextCardState extends State { // look for transcription in message event // if not found, call API to transcribe audio Future getSpeechToText() async { - try { - if (l1Code == null || l2Code == null) { - throw Exception('Language selection not found'); - } - speechToTextResponse ??= - await widget.messageEvent.getSpeechToText(l1Code!, l2Code!); - - debugPrint( - 'Speech to text transcript: ${speechToTextResponse?.transcript.text}', - ); - } catch (e, s) { - error = e; - ErrorHandler.logError( - e: e, - s: s, - data: widget.messageEvent.event.content, - ); - } finally { - setState(() => _fetchingTranscription = false); + // try { + if (l1Code == null || l2Code == null) { + throw Exception('Language selection not found'); } + speechToTextResponse ??= + await widget.messageEvent.getSpeechToText(l1Code!, l2Code!); + + debugPrint( + 'Speech to text transcript: ${speechToTextResponse?.transcript.text}', + ); + // } catch (e, s) { + // debugger(when: kDebugMode); + // error = e; + // ErrorHandler.logError( + // e: e, + // s: s, + // data: widget.messageEvent.event.content, + // ); + // } finally { + setState(() => _fetchingTranscription = false); + // } + } + + TextSpan _buildTranscriptText(BuildContext context) { + final Transcript transcript = speechToTextResponse!.transcript; + final List spans = []; + final String fullText = transcript.text; + int lastEnd = 0; + + for (final token in transcript.sttTokens) { + // debugPrint('Token confidence: ${token.confidence}'); + // debugPrint('color: ${token.color(context)}'); + if (token.offset > lastEnd) { + // Add any plain text before the token + spans.add( + TextSpan( + text: fullText.substring(lastEnd, token.offset), + ), + ); + // debugPrint('Pre: ${fullText.substring(lastEnd, token.offset)}'); + } + + spans.add( + TextSpan( + text: fullText.substring(token.offset, token.offset + token.length), + style: BotStyle.text( + context, + existingStyle: TextStyle(color: token.color(context)), + setColor: false, + ), + // gesturRecognizer that sets selectedToken on click + recognizer: TapGestureRecognizer() + ..onTap = () { + debugPrint('Token tapped'); + debugPrint(token.toJson().toString()); + setState(() { + if (selectedToken == token) { + selectedToken = null; + } else { + selectedToken = token; + } + }); + }, + ), + ); + + // debugPrint( + // 'Main: ${fullText.substring(token.offset, token.offset + token.length)}', + // ); + + lastEnd = token.offset + token.length; + } + + if (lastEnd < fullText.length) { + // Add any remaining text after the last token + spans.add( + TextSpan( + text: fullText.substring(lastEnd), + ), + ); + // debugPrint('Post: ${fullText.substring(lastEnd)}'); + } + + return TextSpan(children: spans); } @override @@ -76,12 +143,37 @@ class MessageSpeechToTextCardState extends State { return CardErrorWidget(error: error); } + final int words = speechToTextResponse!.transcript.sttTokens.length; + final int accuracy = speechToTextResponse!.transcript.confidence; + final int total = words * accuracy; + + //TODO: find better icons return Column( children: [ - SpeechToTextText(transcript: speechToTextResponse!.transcript), - const Divider(), - SpeechToTextScoreWidget( - score: speechToTextResponse!.transcript.confidence, + RichText( + text: _buildTranscriptText(context), + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + IconNumberWidget( + icon: Icons.abc, + number: (selectedToken == null ? words : 1).toString(), + toolTip: L10n.of(context)!.words, + ), + IconNumberWidget( + icon: Icons.approval, + number: + "${selectedToken?.confidence ?? speechToTextResponse!.transcript.confidence}%", + toolTip: L10n.of(context)!.accuracy, + ), + IconNumberWidget( + icon: Icons.score, + number: (selectedToken?.confidence ?? total).toString(), + toolTip: L10n.of(context)!.points, + ), + ], ), ], ); diff --git a/lib/pangea/widgets/chat/speech_to_text_score.dart b/lib/pangea/widgets/chat/speech_to_text_score.dart deleted file mode 100644 index 1e45f73b2..000000000 --- a/lib/pangea/widgets/chat/speech_to_text_score.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/material.dart'; - -class SpeechToTextScoreWidget extends StatefulWidget { - final int score; - const SpeechToTextScoreWidget({super.key, required this.score}); - - @override - SpeechToTextScoreWidgetState createState() => SpeechToTextScoreWidgetState(); -} - -class SpeechToTextScoreWidgetState extends State { - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 500), - transitionBuilder: (Widget child, Animation animation) { - return FadeTransition(opacity: animation, child: child); - }, - child: Text( - 'Score: ${widget.score}', - key: ValueKey(widget.score), - style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold), - ), - ), - ); - } -} diff --git a/lib/pangea/widgets/common/icon_number_widget.dart b/lib/pangea/widgets/common/icon_number_widget.dart new file mode 100644 index 000000000..b42777f91 --- /dev/null +++ b/lib/pangea/widgets/common/icon_number_widget.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; + +class IconNumberWidget extends StatelessWidget { + final IconData icon; + final String number; + final Color? iconColor; + final double? iconSize; + final String? toolTip; + + const IconNumberWidget({ + super.key, + required this.icon, + required this.number, + this.toolTip, + this.iconColor, + this.iconSize, + }); + + Widget _content(BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + icon, + color: iconColor ?? Theme.of(context).iconTheme.color, + size: iconSize ?? Theme.of(context).iconTheme.size, + ), + const SizedBox(width: 8), + Text( + number.toString(), + style: TextStyle( + fontSize: + iconSize ?? Theme.of(context).textTheme.bodyMedium?.fontSize, + color: iconColor ?? Theme.of(context).textTheme.bodyMedium?.color, + ), + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + return toolTip != null + ? Tooltip(message: toolTip!, child: _content(context)) + : _content(context); + } +} diff --git a/needed-translations.txt b/needed-translations.txt index 5d462da84..2aabbf886 100644 --- a/needed-translations.txt +++ b/needed-translations.txt @@ -814,7 +814,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "be": [ @@ -2227,7 +2231,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "bn": [ @@ -3102,7 +3110,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "bo": [ @@ -3977,7 +3989,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "ca": [ @@ -4852,7 +4868,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "cs": [ @@ -5727,7 +5747,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "de": [ @@ -6549,7 +6573,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "el": [ @@ -7424,7 +7452,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "eo": [ @@ -8299,7 +8331,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "es": [ @@ -8322,7 +8358,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "et": [ @@ -9140,7 +9180,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "eu": [ @@ -9958,7 +10002,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "fa": [ @@ -10833,7 +10881,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "fi": [ @@ -11708,7 +11760,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "fr": [ @@ -12583,7 +12639,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "ga": [ @@ -13458,7 +13518,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "gl": [ @@ -14276,7 +14340,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "he": [ @@ -15151,7 +15219,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "hi": [ @@ -16026,7 +16098,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "hr": [ @@ -16888,7 +16964,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "hu": [ @@ -17763,7 +17843,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "ia": [ @@ -19162,7 +19246,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "id": [ @@ -20037,7 +20125,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "ie": [ @@ -20912,7 +21004,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "it": [ @@ -21772,7 +21868,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "ja": [ @@ -22647,7 +22747,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "ko": [ @@ -23522,7 +23626,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "lt": [ @@ -24397,7 +24505,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "lv": [ @@ -25272,7 +25384,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "nb": [ @@ -26147,7 +26263,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "nl": [ @@ -27022,7 +27142,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "pl": [ @@ -27897,7 +28021,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "pt": [ @@ -28772,7 +28900,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "pt_BR": [ @@ -29616,7 +29748,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "pt_PT": [ @@ -30491,7 +30627,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "ro": [ @@ -31366,7 +31506,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "ru": [ @@ -32184,7 +32328,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "sk": [ @@ -33059,7 +33207,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "sl": [ @@ -33934,7 +34086,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "sr": [ @@ -34809,7 +34965,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "sv": [ @@ -35649,7 +35809,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "ta": [ @@ -36524,7 +36688,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "th": [ @@ -37399,7 +37567,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "tr": [ @@ -38259,7 +38431,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "uk": [ @@ -39077,7 +39253,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "vi": [ @@ -39952,7 +40132,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "zh": [ @@ -40770,7 +40954,11 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ], "zh_Hant": [ @@ -41645,6 +41833,10 @@ "commandHint_ignore", "commandHint_unignore", "unreadChatsInApp", - "messageAnalytics" + "messageAnalytics", + "words", + "score", + "accuracy", + "points" ] } From 76544d1f0bda2021ca136e2ddaec8777514b6303 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 7 May 2024 16:48:35 -0400 Subject: [PATCH 6/7] updated speech to text card icons --- lib/pangea/enum/message_mode_enum.dart | 5 +++-- lib/pangea/widgets/chat/message_speech_to_text_card.dart | 5 +++-- pubspec.yaml | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/pangea/enum/message_mode_enum.dart b/lib/pangea/enum/message_mode_enum.dart index e07ee09b7..c64861dc0 100644 --- a/lib/pangea/enum/message_mode_enum.dart +++ b/lib/pangea/enum/message_mode_enum.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:material_symbols_icons/symbols.dart'; enum MessageMode { translation, definition, speechToText, textToSpeech } @@ -9,9 +10,9 @@ extension MessageModeExtension on MessageMode { case MessageMode.translation: return Icons.g_translate; case MessageMode.textToSpeech: - return Icons.play_arrow; + return Symbols.text_to_speech; case MessageMode.speechToText: - return Icons.mic; + return Symbols.speech_to_text; //TODO change icon for audio messages case MessageMode.definition: return Icons.book; diff --git a/lib/pangea/widgets/chat/message_speech_to_text_card.dart b/lib/pangea/widgets/chat/message_speech_to_text_card.dart index 2ec1fa19d..c516d4447 100644 --- a/lib/pangea/widgets/chat/message_speech_to_text_card.dart +++ b/lib/pangea/widgets/chat/message_speech_to_text_card.dart @@ -7,6 +7,7 @@ import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:material_symbols_icons/symbols.dart'; import '../../utils/bot_style.dart'; @@ -163,13 +164,13 @@ class MessageSpeechToTextCardState extends State { toolTip: L10n.of(context)!.words, ), IconNumberWidget( - icon: Icons.approval, + icon: Symbols.target, number: "${selectedToken?.confidence ?? speechToTextResponse!.transcript.confidence}%", toolTip: L10n.of(context)!.accuracy, ), IconNumberWidget( - icon: Icons.score, + icon: Icons.speed, number: (selectedToken?.confidence ?? total).toString(), toolTip: L10n.of(context)!.points, ), diff --git a/pubspec.yaml b/pubspec.yaml index 54cf24972..fbb9c0d7b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -56,7 +56,7 @@ dependencies: git: https://github.com/krille-chan/flutter_shortcuts.git flutter_typeahead: ^5.2.0 flutter_web_auth_2: ^3.1.1 - flutter_webrtc: ^0.9.46 + flutter_webrtc: ^0.10.3 future_loading_dialog: ^0.3.0 geolocator: ^7.6.2 go_router: ^13.2.2 @@ -82,7 +82,7 @@ dependencies: provider: ^6.0.2 punycode: ^1.0.0 qr_code_scanner: ^1.0.1 - receive_sharing_intent: ^1.4.5 + receive_sharing_intent: 1.4.5 # Update needs more work record: 4.4.4 # Upgrade to 5 currently breaks playing on iOS scroll_to_index: ^3.0.1 share_plus: ^8.0.2 @@ -115,6 +115,7 @@ dependencies: in_app_purchase: ^3.1.13 jwt_decode: ^0.3.1 language_tool: ^2.2.0 + material_symbols_icons: ^4.2741.0 open_file: ^3.3.2 purchases_flutter: ^6.26.0 sentry_flutter: ^7.19.0 From 473e02352ac31451cbd55e051c176468ee66739d Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Wed, 8 May 2024 17:22:55 -0400 Subject: [PATCH 7/7] merge with main --- needed-translations.txt | 192 ++++++++++++++++++++++++++++++---------- pubspec.lock | 20 +++-- 2 files changed, 158 insertions(+), 54 deletions(-) diff --git a/needed-translations.txt b/needed-translations.txt index 2aabbf886..c93f52d05 100644 --- a/needed-translations.txt +++ b/needed-translations.txt @@ -14,6 +14,7 @@ "ignoreListDescription", "ignoreUsername", "optionalGroupName", + "writeAMessageFlag", "requests", "allCorrect", "newWayAllGood", @@ -818,7 +819,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "be": [ @@ -1257,6 +1259,7 @@ "wipeChatBackup", "withTheseAddressesRecoveryDescription", "writeAMessage", + "writeAMessageFlag", "yes", "you", "youAreNoLongerParticipatingInThisChat", @@ -2235,7 +2238,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "bn": [ @@ -2259,6 +2263,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -3114,7 +3119,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "bo": [ @@ -3138,6 +3144,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -3993,7 +4000,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "ca": [ @@ -4017,6 +4025,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -4872,7 +4881,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "cs": [ @@ -4896,6 +4906,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -5751,7 +5762,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "de": [ @@ -5771,6 +5783,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "hidePresences", "requests", "allCorrect", @@ -6577,7 +6590,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "el": [ @@ -6601,6 +6615,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -7456,7 +7471,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "eo": [ @@ -7480,6 +7496,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -8335,12 +8352,14 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "es": [ "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "sendReadReceipts", @@ -8362,7 +8381,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "et": [ @@ -8380,6 +8400,7 @@ "ignoreListDescription", "ignoreUsername", "optionalGroupName", + "writeAMessageFlag", "requests", "allCorrect", "newWayAllGood", @@ -9184,7 +9205,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "eu": [ @@ -9202,6 +9224,7 @@ "ignoreListDescription", "ignoreUsername", "optionalGroupName", + "writeAMessageFlag", "requests", "allCorrect", "newWayAllGood", @@ -10006,7 +10029,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "fa": [ @@ -10030,6 +10054,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -10885,7 +10910,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "fi": [ @@ -10909,6 +10935,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -11764,7 +11791,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "fr": [ @@ -11788,6 +11816,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -12643,7 +12672,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "ga": [ @@ -12667,6 +12697,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -13522,7 +13553,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "gl": [ @@ -13540,6 +13572,7 @@ "ignoreListDescription", "ignoreUsername", "optionalGroupName", + "writeAMessageFlag", "requests", "allCorrect", "newWayAllGood", @@ -14344,7 +14377,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "he": [ @@ -14368,6 +14402,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -15223,7 +15258,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "hi": [ @@ -15247,6 +15283,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -16102,7 +16139,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "hr": [ @@ -16123,6 +16161,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -16968,7 +17007,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "hu": [ @@ -16992,6 +17032,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -17847,7 +17888,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "ia": [ @@ -18272,6 +18314,7 @@ "wipeChatBackup", "withTheseAddressesRecoveryDescription", "writeAMessage", + "writeAMessageFlag", "yes", "you", "youAreNoLongerParticipatingInThisChat", @@ -19250,7 +19293,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "id": [ @@ -19274,6 +19318,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -20129,7 +20174,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "ie": [ @@ -20153,6 +20199,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -21008,7 +21055,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "it": [ @@ -21028,6 +21076,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -21872,7 +21921,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "ja": [ @@ -21896,6 +21946,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -22751,7 +22802,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "ko": [ @@ -22775,6 +22827,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -23630,7 +23683,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "lt": [ @@ -23654,6 +23708,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -24509,7 +24564,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "lv": [ @@ -24533,6 +24589,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -25388,7 +25445,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "nb": [ @@ -25412,6 +25470,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -26267,7 +26326,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "nl": [ @@ -26291,6 +26351,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -27146,7 +27207,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "pl": [ @@ -27170,6 +27232,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -28025,7 +28088,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "pt": [ @@ -28049,6 +28113,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -28904,7 +28969,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "pt_BR": [ @@ -28924,6 +28990,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -29752,7 +29819,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "pt_PT": [ @@ -29776,6 +29844,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -30631,7 +30700,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "ro": [ @@ -30655,6 +30725,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -31510,7 +31581,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "ru": [ @@ -31528,6 +31600,7 @@ "ignoreListDescription", "ignoreUsername", "optionalGroupName", + "writeAMessageFlag", "requests", "allCorrect", "newWayAllGood", @@ -32332,7 +32405,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "sk": [ @@ -32356,6 +32430,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -33211,7 +33286,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "sl": [ @@ -33235,6 +33311,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -34090,7 +34167,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "sr": [ @@ -34114,6 +34192,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -34969,7 +35048,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "sv": [ @@ -34989,6 +35069,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -35813,7 +35894,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "ta": [ @@ -35837,6 +35919,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -36692,7 +36775,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "th": [ @@ -36716,6 +36800,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -37571,7 +37656,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "tr": [ @@ -37591,6 +37677,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -38435,7 +38522,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "uk": [ @@ -38453,6 +38541,7 @@ "ignoreListDescription", "ignoreUsername", "optionalGroupName", + "writeAMessageFlag", "requests", "allCorrect", "newWayAllGood", @@ -39257,7 +39346,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "vi": [ @@ -39281,6 +39371,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -40136,7 +40227,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "zh": [ @@ -40154,6 +40246,7 @@ "ignoreListDescription", "ignoreUsername", "optionalGroupName", + "writeAMessageFlag", "requests", "allCorrect", "newWayAllGood", @@ -40958,7 +41051,8 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ], "zh_Hant": [ @@ -40982,6 +41076,7 @@ "optionalGroupName", "presenceStyle", "presencesToggle", + "writeAMessageFlag", "youInvitedToBy", "hidePresences", "requests", @@ -41837,6 +41932,7 @@ "words", "score", "accuracy", - "points" + "points", + "noPaymentInfo" ] } diff --git a/pubspec.lock b/pubspec.lock index ac7f45e8d..54e1ac6a7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -293,10 +293,10 @@ packages: dependency: transitive description: name: dart_webrtc - sha256: "5897a3bdd6c7fded07e80e250260ca4c9cd61f9080911aa308b516e1206745a9" + sha256: b3a4f109c551a10170ece8fc79b5ca1b98223f24bcebc0f971d7fe35daad7a3b url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.4.4" dbus: dependency: transitive description: @@ -870,10 +870,10 @@ packages: dependency: "direct main" description: name: flutter_webrtc - sha256: "577216727181cb13776a65d3e7cb33e783e740c5496335011aed4a038b28c3fe" + sha256: "63295f3aaba6e0e2520064a928fd0b79bf3636695af9c89cb219241994a45bf6" url: "https://pub.dev" source: hosted - version: "0.9.47" + version: "0.10.4" freezed_annotation: dependency: transitive description: @@ -1397,6 +1397,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.0" + material_symbols_icons: + dependency: "direct main" + description: + name: material_symbols_icons + sha256: "36d4e5dd72f2fd282aca127cc4c4c29786d702cb506231ea73a5497fc324bf46" + url: "https://pub.dev" + source: hosted + version: "4.2741.0" matrix: dependency: "direct main" description: @@ -2614,10 +2622,10 @@ packages: dependency: "direct main" description: name: webrtc_interface - sha256: "2efbd3e4e5ebeb2914253bcc51dafd3053c4b87b43f3076c74835a9deecbae3a" + sha256: abec3ab7956bd5ac539cf34a42fa0c82ea26675847c0966bb85160400eea9388 url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.2.0" win32: dependency: transitive description: