Merge pull request #3143 from pangeachat/3140-audio-phonetics-briefly-shows-transcription

chore: show loading indicator and error message in transcription
pull/2245/head
ggurdin 5 months ago committed by GitHub
commit 7bd9179cda
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -5031,5 +5031,6 @@
"type": "int" "type": "int"
} }
} }
} },
"failedToFetchTranscription": "Failed to fetch transcription"
} }

@ -9,6 +9,7 @@ import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_repo.dart'; import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_repo.dart';
import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_request.dart'; import 'package:fluffychat/pangea/phonetic_transcription/phonetic_transcription_request.dart';
import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart'; import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart';
import 'package:fluffychat/widgets/hover_builder.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
class PhoneticTranscriptionWidget extends StatefulWidget { class PhoneticTranscriptionWidget extends StatefulWidget {
@ -32,51 +33,61 @@ class PhoneticTranscriptionWidget extends StatefulWidget {
class _PhoneticTranscriptionWidgetState class _PhoneticTranscriptionWidgetState
extends State<PhoneticTranscriptionWidget> { extends State<PhoneticTranscriptionWidget> {
late Future<String?> _transcriptionFuture;
bool _hovering = false;
bool _isPlaying = false; bool _isPlaying = false;
bool _isLoading = false; bool _isLoading = false;
late final StreamSubscription _loadingChoreoSubscription; Object? _error;
String? _transcription;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_transcriptionFuture = _fetchTranscription(); _fetchTranscription();
_loadingChoreoSubscription =
TtsController.loadingChoreoStream.stream.listen((val) {
if (mounted) setState(() => _isLoading = val);
});
} }
@override Future<void> _fetchTranscription() async {
void dispose() { try {
TtsController.stop(); setState(() {
_loadingChoreoSubscription.cancel(); _isLoading = true;
super.dispose(); _error = null;
} _transcription = null;
});
Future<String?> _fetchTranscription() async { if (MatrixState.pangeaController.languageController.userL1 == null) {
if (MatrixState.pangeaController.languageController.userL1 == null) { ErrorHandler.logError(
e: Exception('User L1 is not set'),
data: {
'text': widget.text,
'textLanguageCode': widget.textLanguage.langCode,
},
);
_error = Exception('User L1 is not set');
return;
}
final req = PhoneticTranscriptionRequest(
arc: LanguageArc(
l1: MatrixState.pangeaController.languageController.userL1!,
l2: widget.textLanguage,
),
content: PangeaTokenText.fromString(widget.text),
// arc can be omitted for default empty map
);
final res = await PhoneticTranscriptionRepo.get(req);
_transcription = res.phoneticTranscriptionResult.phoneticTranscription
.first.phoneticL1Transcription.content;
} catch (e, s) {
_error = e;
ErrorHandler.logError( ErrorHandler.logError(
e: Exception('User L1 is not set'), e: e,
s: s,
data: { data: {
'text': widget.text, 'text': widget.text,
'textLanguageCode': widget.textLanguage.langCode, 'textLanguageCode': widget.textLanguage.langCode,
}, },
); );
return widget.text; // Fallback to original text if no L1 is set } finally {
if (mounted) setState(() => _isLoading = false);
} }
final req = PhoneticTranscriptionRequest(
arc: LanguageArc(
l1: MatrixState.pangeaController.languageController.userL1!,
l2: widget.textLanguage,
),
content: PangeaTokenText.fromString(widget.text),
// arc can be omitted for default empty map
);
final res = await PhoneticTranscriptionRepo.get(req);
return res.phoneticTranscriptionResult.phoneticTranscription.first
.phoneticL1Transcription.content;
} }
Future<void> _handleAudioTap(BuildContext context) async { Future<void> _handleAudioTap(BuildContext context) async {
@ -101,55 +112,66 @@ class _PhoneticTranscriptionWidgetState
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FutureBuilder<String?>( return HoverBuilder(
future: _transcriptionFuture, builder: (context, hovering) {
builder: (context, snapshot) { return GestureDetector(
final transcription = snapshot.data ?? ''; onTap: () => _handleAudioTap(context),
return MouseRegion( child: AnimatedContainer(
onEnter: (_) => setState(() => _hovering = true), duration: const Duration(milliseconds: 150),
onExit: (_) => setState(() => _hovering = false), decoration: BoxDecoration(
child: GestureDetector( color: hovering
onTap: () => _handleAudioTap(context), ? Colors.grey.withAlpha((0.2 * 255).round())
child: AnimatedContainer( : Colors.transparent,
duration: const Duration(milliseconds: 150), borderRadius: BorderRadius.circular(6),
decoration: BoxDecoration( ),
color: _hovering padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
? Colors.grey.withAlpha((0.2 * 255).round()) child: Row(
: Colors.transparent, mainAxisSize: MainAxisSize.min,
borderRadius: BorderRadius.circular(6), children: [
), if (_error != null)
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), Row(
child: Row( spacing: 8.0,
mainAxisSize: MainAxisSize.min, children: [
children: [ Icon(
Icons.error_outline,
size: widget.iconSize ?? 24,
color: Theme.of(context).colorScheme.error,
),
Text(
L10n.of(context).failedToFetchTranscription,
style: widget.style,
),
],
)
else if (_isLoading || _transcription == null)
const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator.adaptive(),
)
else
Flexible( Flexible(
child: Text( child: Text(
"/${transcription.isNotEmpty ? transcription : widget.text}/", "/$_transcription/",
style: widget.style ?? style: widget.style ??
Theme.of(context).textTheme.bodyMedium, Theme.of(context).textTheme.bodyMedium,
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
if (_transcription != null && _error == null)
Tooltip( Tooltip(
message: _isPlaying message: _isPlaying
? L10n.of(context).stop ? L10n.of(context).stop
: L10n.of(context).playAudio, : L10n.of(context).playAudio,
child: _isLoading child: Icon(
? const SizedBox( _isPlaying ? Icons.pause_outlined : Icons.volume_up,
width: 16, size: widget.iconSize ?? 24,
height: 16, color: _isPlaying
child: CircularProgressIndicator(strokeWidth: 3), ? Theme.of(context).colorScheme.primary
) : Theme.of(context).iconTheme.color,
: Icon( ),
_isPlaying ? Icons.pause_outlined : Icons.volume_up,
size: widget.iconSize ?? 24,
color: _isPlaying
? Theme.of(context).colorScheme.primary
: Theme.of(context).iconTheme.color,
),
), ),
], ],
),
), ),
), ),
); );

Loading…
Cancel
Save