chore: send audio event and request for transcription concurrently (#5687)

This commit is contained in:
ggurdin 2026-02-13 09:49:47 -05:00 committed by GitHub
parent 229370abc7
commit f00115f712
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 80 additions and 48 deletions

View file

@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:async/async.dart' as async;
import 'package:collection/collection.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
@ -65,6 +66,10 @@ import 'package:fluffychat/pangea/learning_settings/language_mismatch_repo.dart'
import 'package:fluffychat/pangea/learning_settings/p_language_dialog.dart';
import 'package:fluffychat/pangea/navigation/navigation_util.dart';
import 'package:fluffychat/pangea/spaces/load_participants_builder.dart';
import 'package:fluffychat/pangea/speech_to_text/audio_encoding_enum.dart';
import 'package:fluffychat/pangea/speech_to_text/speech_to_text_repo.dart';
import 'package:fluffychat/pangea/speech_to_text/speech_to_text_request_model.dart';
import 'package:fluffychat/pangea/speech_to_text/speech_to_text_response_model.dart';
import 'package:fluffychat/pangea/subscription/widgets/paywall_card.dart';
import 'package:fluffychat/pangea/token_info_feedback/show_token_feedback_dialog.dart';
import 'package:fluffychat/pangea/token_info_feedback/token_info_feedback_request.dart';
@ -1207,7 +1212,11 @@ class ChatController extends State<ChatPageWithRoom>
replyEvent.value = null;
// Pangea#
room
// #Pangea
final transcriptFuture = _getVoiceMessageTranscript(file);
// room
final eventFuture = room
// Pangea#
.sendFileEvent(
file,
// #Pangea
@ -1229,7 +1238,6 @@ class ChatController extends State<ChatPageWithRoom>
},
// #Pangea
)
.then(_sendVoiceMessageAnalytics)
.catchError((e, s) {
ErrorHandler.logError(
e: e,
@ -1247,6 +1255,39 @@ class ChatController extends State<ChatPageWithRoom>
// );
// return null;
// });
Future.wait([eventFuture, transcriptFuture]).then((results) async {
final eventId = results[0] as String?;
final transcript = results[1] as async.Result<SpeechToTextResponseModel>;
if (eventId == null) {
ErrorHandler.logError(
e: Exception('eventID null in voiceMessageAction'),
s: StackTrace.current,
data: {'roomId': roomId},
);
return;
}
if (transcript.result == null) return;
final stt = transcript.result!;
final event = await room.getEventById(eventId);
if (event == null) {
ErrorHandler.logError(
e: Exception('Event not found after sending voice message'),
s: StackTrace.current,
data: {'roomId': roomId},
);
} else {
final messageEvent = PangeaMessageEvent(
event: event,
timeline: timeline!,
ownMessage: true,
);
messageEvent.sendSttRepresentationEvent(stt);
}
_sendVoiceMessageAnalytics(eventId, stt);
});
// Pangea#
return;
}
@ -2191,39 +2232,11 @@ class ChatController extends State<ChatPageWithRoom>
}
}
Future<void> _sendVoiceMessageAnalytics(String? eventId) async {
if (eventId == null) {
ErrorHandler.logError(
e: Exception('eventID null in voiceMessageAction'),
s: StackTrace.current,
data: {'roomId': roomId},
);
return;
}
final event = await room.getEventById(eventId);
if (event == null) {
ErrorHandler.logError(
e: Exception('Event not found after sending voice message'),
s: StackTrace.current,
data: {'roomId': roomId},
);
return;
}
Future<void> _sendVoiceMessageAnalytics(
String eventId,
SpeechToTextResponseModel stt,
) async {
try {
final messageEvent = PangeaMessageEvent(
event: event,
timeline: timeline!,
ownMessage: true,
);
final stt = await messageEvent.requestSpeechToText(
MatrixState.pangeaController.userController.userL1?.langCodeShort ??
LanguageKeys.unknownLanguage,
MatrixState.pangeaController.userController.userL2?.langCodeShort ??
LanguageKeys.unknownLanguage,
);
if (stt.transcript.sttTokens.isEmpty) return;
final constructs = stt.constructs(roomId, eventId);
if (constructs.isEmpty) return;
@ -2241,6 +2254,35 @@ class ChatController extends State<ChatPageWithRoom>
}
}
Future<async.Result<SpeechToTextResponseModel>> _getVoiceMessageTranscript(
MatrixAudioFile file,
) async {
return SpeechToTextRepo.get(
MatrixState.pangeaController.userController.accessToken,
SpeechToTextRequestModel(
audioContent: file.bytes,
config: SpeechToTextAudioConfigModel(
encoding: mimeTypeToAudioEncoding(file.mimeType),
sampleRateHertz: 22050,
userL1:
MatrixState
.pangeaController
.userController
.userL1
?.langCodeShort ??
LanguageKeys.unknownLanguage,
userL2:
MatrixState
.pangeaController
.userController
.userL2
?.langCodeShort ??
LanguageKeys.unknownLanguage,
),
),
);
}
void showNextMatch() {
MatrixState.pAnyState.closeOverlay();
final match = choreographer.igcController.openMatches.firstOrNull;

View file

@ -327,17 +327,7 @@ class PangeaChatInputRow extends StatelessWidget {
.value
? IconButton(
tooltip: L10n.of(context).voiceMessage,
onPressed: () => ScaffoldMessenger.of(context)
.showSnackBar(
SnackBar(
content: Text(
L10n.of(
context,
).longPressToRecordVoiceMessage,
),
),
),
onLongPress: () => recordingViewModel
onPressed: () => recordingViewModel
.startRecording(controller.room),
style: IconButton.styleFrom(
backgroundColor: theme.bubbleColor,

View file

@ -444,13 +444,13 @@ class PangeaMessageEvent {
}
if (sendEvent) {
_sendSttRepresentationEvent(result.result!);
sendSttRepresentationEvent(result.result!);
}
return result.result!;
}
Future<Event?> _sendSttRepresentationEvent(
Future<Event?> sendSttRepresentationEvent(
SpeechToTextResponseModel stt,
) async {
final representation = PangeaRepresentation(
@ -486,7 +486,7 @@ class PangeaMessageEvent {
SpeechToTextResponseModel? stt = rep?.content.speechToText;
if (rep == null) {
stt ??= await requestSpeechToText(l1Code, l2Code, sendEvent: false);
final repEvent = await _sendSttRepresentationEvent(stt);
final repEvent = await sendSttRepresentationEvent(stt);
if (repEvent == null) {
throw Exception("Failed to send representation event for STT");
}