From 4a77e6fb53066f0a598cb2aff30e7bb001caf167 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Mon, 6 May 2024 15:10:15 -0400 Subject: [PATCH] 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",