1846 word specific audio player not working (#1882)
* feat: tie TTS enabled to target lang, show warning popup when disabled * fix: prevent top overflow for popups
This commit is contained in:
parent
0255a71929
commit
62d5a7190f
13 changed files with 243 additions and 213 deletions
|
|
@ -4824,5 +4824,7 @@
|
|||
"whoIsAllowedToJoinThisChat": "Who is allowed to join this chat",
|
||||
"dontForgetPassword": "Don't forget your password!",
|
||||
"enableAutocorrectToolName": "Enable autocorrect",
|
||||
"enableAutocorrectDescription": "Use your keyboard's built-in autocorrect when typing messages"
|
||||
"enableAutocorrectDescription": "Use your keyboard's built-in autocorrect when typing messages",
|
||||
"ttsDisbledTitle": "Text-to-speech disabled",
|
||||
"ttsDisabledBody": "You can enable text-to-speech in your learning settings"
|
||||
}
|
||||
|
|
@ -100,7 +100,11 @@ class ChoicesArrayState extends State<ChoicesArray> {
|
|||
widget.onPressed(value, index);
|
||||
// TODO - what to pass here as eventID?
|
||||
if (widget.enableAudio && widget.tts != null) {
|
||||
widget.tts?.tryToSpeak(value, context, null);
|
||||
widget.tts?.tryToSpeak(
|
||||
value,
|
||||
context,
|
||||
targetID: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
: (String value, int index) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ class OverlayUtil {
|
|||
OverlayPositionEnum position = OverlayPositionEnum.transform,
|
||||
Offset? offset,
|
||||
String? overlayKey,
|
||||
Alignment? targetAnchor,
|
||||
Alignment? followerAnchor,
|
||||
}) {
|
||||
try {
|
||||
if (closePrevOverlay) {
|
||||
|
|
@ -56,8 +58,9 @@ class OverlayUtil {
|
|||
child: (position != OverlayPositionEnum.transform)
|
||||
? child
|
||||
: CompositedTransformFollower(
|
||||
targetAnchor: Alignment.topCenter,
|
||||
followerAnchor: Alignment.bottomCenter,
|
||||
targetAnchor: targetAnchor ?? Alignment.topCenter,
|
||||
followerAnchor:
|
||||
followerAnchor ?? Alignment.bottomCenter,
|
||||
link: MatrixState.pAnyState
|
||||
.layerLinkAndKey(transformTargetId)
|
||||
.link,
|
||||
|
|
@ -110,6 +113,8 @@ class OverlayUtil {
|
|||
Offset offset = Offset.zero;
|
||||
final RenderBox? targetRenderBox =
|
||||
layerLinkAndKey.key.currentContext!.findRenderObject() as RenderBox?;
|
||||
|
||||
bool hasTopOverflow = false;
|
||||
if (targetRenderBox != null && targetRenderBox.hasSize) {
|
||||
final Offset transformTargetOffset =
|
||||
(targetRenderBox).localToGlobal(Offset.zero);
|
||||
|
|
@ -117,10 +122,15 @@ class OverlayUtil {
|
|||
final horizontalMidpoint =
|
||||
transformTargetOffset.dx + (transformTargetSize.width / 2);
|
||||
|
||||
final verticalMidpoint =
|
||||
transformTargetOffset.dy + (transformTargetSize.height / 2);
|
||||
debugPrint("vertical midpoint $verticalMidpoint");
|
||||
|
||||
final halfMaxWidth = maxWidth / 2;
|
||||
final hasLeftOverflow = (horizontalMidpoint - halfMaxWidth) < 0;
|
||||
final hasRightOverflow = (horizontalMidpoint + halfMaxWidth) >
|
||||
MediaQuery.of(context).size.width;
|
||||
hasTopOverflow = (verticalMidpoint - maxHeight) < 0;
|
||||
|
||||
double xOffset = 0;
|
||||
|
||||
|
|
@ -156,6 +166,10 @@ class OverlayUtil {
|
|||
closePrevOverlay: closePrevOverlay,
|
||||
offset: offset,
|
||||
overlayKey: overlayKey,
|
||||
targetAnchor:
|
||||
hasTopOverflow ? Alignment.bottomCenter : Alignment.topCenter,
|
||||
followerAnchor:
|
||||
hasTopOverflow ? Alignment.topCenter : Alignment.bottomCenter,
|
||||
);
|
||||
} catch (err, stack) {
|
||||
debugger(when: kDebugMode);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ enum InstructionsEnum {
|
|||
unlockedLanguageTools,
|
||||
lemmaMeaning,
|
||||
activityPlannerOverview,
|
||||
ttsDisabled,
|
||||
}
|
||||
|
||||
extension InstructionsEnumExtension on InstructionsEnum {
|
||||
|
|
@ -37,6 +38,8 @@ extension InstructionsEnumExtension on InstructionsEnum {
|
|||
return l10n.tooltipInstructionsTitle;
|
||||
case InstructionsEnum.missingVoice:
|
||||
return l10n.missingVoiceTitle;
|
||||
case InstructionsEnum.ttsDisabled:
|
||||
return l10n.ttsDisbledTitle;
|
||||
case InstructionsEnum.activityPlannerOverview:
|
||||
case InstructionsEnum.clickAgainToDeselect:
|
||||
case InstructionsEnum.speechToText:
|
||||
|
|
@ -87,6 +90,8 @@ extension InstructionsEnumExtension on InstructionsEnum {
|
|||
return l10n.lemmaMeaningInstructionsBody;
|
||||
case InstructionsEnum.activityPlannerOverview:
|
||||
return l10n.activityPlannerOverviewInstructionsBody;
|
||||
case InstructionsEnum.ttsDisabled:
|
||||
return l10n.ttsDisabledBody;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ class SettingsLearningController extends State<SettingsLearning> {
|
|||
|
||||
Future<void> submit() async {
|
||||
if (formKey.currentState!.validate()) {
|
||||
if (!isTTSSupported) {
|
||||
updateToolSetting(ToolSetting.enableTTS, false);
|
||||
}
|
||||
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () async => pangeaController.userController.updateProfile(
|
||||
|
|
@ -62,6 +66,9 @@ class SettingsLearningController extends State<SettingsLearning> {
|
|||
}
|
||||
if (targetLanguage != null) {
|
||||
_profile.userSettings.targetLanguage = targetLanguage.langCode;
|
||||
if (!_profile.toolSettings.enableTTS && isTTSSupported) {
|
||||
updateToolSetting(ToolSetting.enableTTS, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (mounted) setState(() {});
|
||||
|
|
@ -123,12 +130,18 @@ class SettingsLearningController extends State<SettingsLearning> {
|
|||
case ToolSetting.autoIGC:
|
||||
return toolSettings.autoIGC;
|
||||
case ToolSetting.enableTTS:
|
||||
return toolSettings.enableTTS;
|
||||
return _profile.userSettings.targetLanguage != null &&
|
||||
tts.isLanguageSupported(_profile.userSettings.targetLanguage!) &&
|
||||
toolSettings.enableTTS;
|
||||
case ToolSetting.enableAutocorrect:
|
||||
return toolSettings.enableAutocorrect;
|
||||
}
|
||||
}
|
||||
|
||||
bool get isTTSSupported =>
|
||||
_profile.userSettings.targetLanguage != null &&
|
||||
tts.isLanguageSupported(_profile.userSettings.targetLanguage!);
|
||||
|
||||
LanguageModel? get selectedSourceLanguage {
|
||||
return userL1 ?? pangeaController.languageController.systemLanguage;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,8 +118,7 @@ class SettingsLearningView extends StatelessWidget {
|
|||
title: toolSetting.toolName(context),
|
||||
subtitle: toolSetting ==
|
||||
ToolSetting.enableTTS &&
|
||||
!controller
|
||||
.tts.isLanguageFullySupported
|
||||
!controller.isTTSSupported
|
||||
? null
|
||||
: toolSetting
|
||||
.toolDescription(context),
|
||||
|
|
@ -130,54 +129,64 @@ class SettingsLearningView extends StatelessWidget {
|
|||
),
|
||||
enabled:
|
||||
toolSetting == ToolSetting.enableTTS
|
||||
? controller
|
||||
.tts.isLanguageFullySupported
|
||||
? controller.isTTSSupported
|
||||
: true,
|
||||
),
|
||||
if (toolSetting == ToolSetting.enableTTS &&
|
||||
!controller
|
||||
.tts.isLanguageFullySupported)
|
||||
ListTile(
|
||||
trailing: const Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
!controller.isTTSSupported)
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 16.0,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.info_outlined,
|
||||
color: Theme.of(context)
|
||||
.disabledColor,
|
||||
),
|
||||
),
|
||||
child: Icon(Icons.info_outlined),
|
||||
),
|
||||
subtitle: RichText(
|
||||
text: TextSpan(
|
||||
text: L10n.of(context)
|
||||
.couldNotFindTTS,
|
||||
style: DefaultTextStyle.of(context)
|
||||
.style,
|
||||
children: [
|
||||
if (PlatformInfos.isWindows ||
|
||||
PlatformInfos.isAndroid)
|
||||
TextSpan(
|
||||
text: L10n.of(context)
|
||||
.ttsInstructionsHyperlink,
|
||||
style: const TextStyle(
|
||||
color: Colors.blue,
|
||||
fontWeight: FontWeight.bold,
|
||||
decoration: TextDecoration
|
||||
.underline,
|
||||
),
|
||||
recognizer:
|
||||
TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
launchUrlString(
|
||||
PlatformInfos
|
||||
.isWindows
|
||||
? AppConfig
|
||||
.windowsTTSDownloadInstructions
|
||||
: AppConfig
|
||||
.androidTTSDownloadInstructions,
|
||||
);
|
||||
},
|
||||
Flexible(
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: L10n.of(context)
|
||||
.couldNotFindTTS,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.disabledColor,
|
||||
),
|
||||
],
|
||||
children: [
|
||||
if (PlatformInfos.isWindows ||
|
||||
PlatformInfos.isAndroid)
|
||||
TextSpan(
|
||||
text: L10n.of(context)
|
||||
.ttsInstructionsHyperlink,
|
||||
style: const TextStyle(
|
||||
color: Colors.blue,
|
||||
fontWeight:
|
||||
FontWeight.bold,
|
||||
decoration:
|
||||
TextDecoration
|
||||
.underline,
|
||||
),
|
||||
recognizer:
|
||||
TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
launchUrlString(
|
||||
PlatformInfos
|
||||
.isWindows
|
||||
? AppConfig
|
||||
.windowsTTSDownloadInstructions
|
||||
: AppConfig
|
||||
.androidTTSDownloadInstructions,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -91,18 +91,6 @@ class TtsController {
|
|||
s: s,
|
||||
data: {},
|
||||
);
|
||||
} finally {
|
||||
debugPrint("availableLangCodes: $_availableLangCodes");
|
||||
final enableTTSSetting = userController.profile.toolSettings.enableTTS;
|
||||
if (enableTTSSetting != isLanguageFullySupported) {
|
||||
await userController.updateProfile(
|
||||
(profile) {
|
||||
profile.toolSettings.enableTTS = isLanguageFullySupported;
|
||||
return profile;
|
||||
},
|
||||
waitForDataInSync: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -162,47 +150,49 @@ class TtsController {
|
|||
|
||||
Future<void> _showMissingVoicePopup(
|
||||
BuildContext context,
|
||||
String eventID,
|
||||
) async {
|
||||
await instructionsShowPopup(
|
||||
context,
|
||||
InstructionsEnum.missingVoice,
|
||||
eventID,
|
||||
showToggle: false,
|
||||
customContent: const Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: MissingVoiceButton(),
|
||||
),
|
||||
forceShow: true,
|
||||
);
|
||||
return;
|
||||
}
|
||||
String targetID,
|
||||
) async =>
|
||||
instructionsShowPopup(
|
||||
context,
|
||||
InstructionsEnum.missingVoice,
|
||||
targetID,
|
||||
showToggle: false,
|
||||
customContent: const Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: MissingVoiceButton(),
|
||||
),
|
||||
forceShow: true,
|
||||
);
|
||||
|
||||
Future<void> _showTTSDisabledPopup(
|
||||
BuildContext context,
|
||||
String targetID,
|
||||
) async =>
|
||||
instructionsShowPopup(
|
||||
context,
|
||||
InstructionsEnum.ttsDisabled,
|
||||
targetID,
|
||||
showToggle: false,
|
||||
forceShow: true,
|
||||
);
|
||||
|
||||
/// A safer version of speak, that handles the case of
|
||||
/// the language not being supported by the TTS engine
|
||||
Future<void> tryToSpeak(
|
||||
String text,
|
||||
BuildContext context,
|
||||
// TODO - make non-nullable again
|
||||
String? eventID,
|
||||
) async {
|
||||
if (!MatrixState
|
||||
.pangeaController.userController.profile.toolSettings.enableTTS) {
|
||||
return;
|
||||
}
|
||||
BuildContext context, {
|
||||
// Target ID for where to show warning popup
|
||||
String? targetID,
|
||||
}) async {
|
||||
final enableTTS = MatrixState
|
||||
.pangeaController.userController.profile.toolSettings.enableTTS;
|
||||
|
||||
if (isLanguageFullySupported) {
|
||||
if (_isL2FullySupported && enableTTS) {
|
||||
await _speak(text);
|
||||
} else {
|
||||
ErrorHandler.logError(
|
||||
e: 'Language not supported by TTS engine',
|
||||
data: {
|
||||
'targetLanguage': targetLanguage,
|
||||
},
|
||||
);
|
||||
if (eventID != null) {
|
||||
await _showMissingVoicePopup(context, eventID);
|
||||
}
|
||||
} else if (!_isL2FullySupported && targetID != null) {
|
||||
await _showMissingVoicePopup(context, targetID);
|
||||
} else if (!enableTTS && targetID != null) {
|
||||
await _showTTSDisabledPopup(context, targetID);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -252,6 +242,8 @@ class TtsController {
|
|||
}
|
||||
}
|
||||
|
||||
bool get isLanguageFullySupported =>
|
||||
_availableLangCodes.contains(targetLanguage);
|
||||
bool get _isL2FullySupported => _availableLangCodes.contains(targetLanguage);
|
||||
|
||||
bool isLanguageSupported(String langCode) =>
|
||||
_availableLangCodes.contains(langCode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,18 +69,6 @@ class MessageAudioCardState extends State<MessageAudioCard> {
|
|||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
Future<void> playSelectionAudio() async {
|
||||
if (widget.selection == null) return;
|
||||
final PangeaTokenText selection = widget.selection!;
|
||||
final tokenText = selection.content;
|
||||
|
||||
await widget.tts.tryToSpeak(
|
||||
tokenText,
|
||||
context,
|
||||
widget.messageEvent.eventId,
|
||||
);
|
||||
}
|
||||
|
||||
void setSectionStartAndEnd(int? start, int? end) => mounted
|
||||
? setState(() {
|
||||
sectionStartMS = start;
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
widget.chatController.choreographer.tts.tryToSpeak(
|
||||
selectedSpan.content,
|
||||
context,
|
||||
pangeaMessageEvent?.eventId,
|
||||
targetID: null,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -226,7 +226,6 @@ class MultipleChoiceActivityState extends State<MultipleChoiceActivity> {
|
|||
WordAudioButton(
|
||||
text: practiceActivity.content.answers.first,
|
||||
ttsController: tts,
|
||||
eventID: widget.event.eventId,
|
||||
),
|
||||
if (practiceActivity.activityType ==
|
||||
ActivityTypeEnum.hiddenWordListening)
|
||||
|
|
|
|||
|
|
@ -4,18 +4,17 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class WordAudioButton extends StatefulWidget {
|
||||
final String text;
|
||||
final TtsController ttsController;
|
||||
final String? eventID;
|
||||
final double size;
|
||||
|
||||
const WordAudioButton({
|
||||
super.key,
|
||||
required this.text,
|
||||
required this.ttsController,
|
||||
this.eventID,
|
||||
this.size = 24,
|
||||
});
|
||||
|
||||
|
|
@ -28,45 +27,49 @@ class WordAudioButtonState extends State<WordAudioButton> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IconButton(
|
||||
icon: const Icon(Icons.play_arrow_outlined),
|
||||
isSelected: _isPlaying,
|
||||
selectedIcon: const Icon(Icons.pause_outlined),
|
||||
color: _isPlaying ? Colors.white : null,
|
||||
tooltip: _isPlaying ? L10n.of(context).stop : L10n.of(context).playAudio,
|
||||
iconSize: widget.size,
|
||||
onPressed: () async {
|
||||
if (_isPlaying) {
|
||||
await widget.ttsController.stop();
|
||||
if (mounted) {
|
||||
setState(() => _isPlaying = false);
|
||||
}
|
||||
} else {
|
||||
if (mounted) {
|
||||
setState(() => _isPlaying = true);
|
||||
}
|
||||
try {
|
||||
await widget.ttsController.tryToSpeak(
|
||||
widget.text,
|
||||
context,
|
||||
widget.eventID,
|
||||
);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"text": widget.text,
|
||||
"eventID": widget.eventID,
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
return CompositedTransformTarget(
|
||||
link: MatrixState.pAnyState.layerLinkAndKey('word-audio-button').link,
|
||||
child: IconButton(
|
||||
key: MatrixState.pAnyState.layerLinkAndKey('word-audio-button').key,
|
||||
icon: const Icon(Icons.play_arrow_outlined),
|
||||
isSelected: _isPlaying,
|
||||
selectedIcon: const Icon(Icons.pause_outlined),
|
||||
color: _isPlaying ? Colors.white : null,
|
||||
tooltip:
|
||||
_isPlaying ? L10n.of(context).stop : L10n.of(context).playAudio,
|
||||
iconSize: widget.size,
|
||||
onPressed: () async {
|
||||
if (_isPlaying) {
|
||||
await widget.ttsController.stop();
|
||||
if (mounted) {
|
||||
setState(() => _isPlaying = false);
|
||||
}
|
||||
} else {
|
||||
if (mounted) {
|
||||
setState(() => _isPlaying = true);
|
||||
}
|
||||
try {
|
||||
await widget.ttsController.tryToSpeak(
|
||||
widget.text,
|
||||
context,
|
||||
targetID: 'word-audio-button',
|
||||
);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"text": widget.text,
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() => _isPlaying = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, // Disable button if language isn't supported
|
||||
}, // Disable button if language isn't supported
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,16 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class WordTextWithAudioButton extends StatefulWidget {
|
||||
final String text;
|
||||
final TtsController ttsController;
|
||||
final String eventID;
|
||||
|
||||
const WordTextWithAudioButton({
|
||||
super.key,
|
||||
required this.text,
|
||||
required this.ttsController,
|
||||
required this.eventID,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -24,73 +23,76 @@ class WordAudioButtonState extends State<WordTextWithAudioButton> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
onEnter: (event) => setState(() {}),
|
||||
onExit: (event) => setState(() {}),
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
if (_isPlaying) {
|
||||
await widget.ttsController.stop();
|
||||
if (mounted) {
|
||||
setState(() => _isPlaying = false);
|
||||
}
|
||||
} else {
|
||||
if (mounted) {
|
||||
setState(() => _isPlaying = true);
|
||||
}
|
||||
try {
|
||||
await widget.ttsController.tryToSpeak(
|
||||
widget.text,
|
||||
context,
|
||||
widget.eventID,
|
||||
);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"text": widget.text,
|
||||
"eventID": widget.eventID,
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
return CompositedTransformTarget(
|
||||
link: MatrixState.pAnyState.layerLinkAndKey('text-audio-button').link,
|
||||
child: MouseRegion(
|
||||
key: MatrixState.pAnyState.layerLinkAndKey('text-audio-button').key,
|
||||
cursor: SystemMouseCursors.click,
|
||||
onEnter: (event) => setState(() {}),
|
||||
onExit: (event) => setState(() {}),
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
if (_isPlaying) {
|
||||
await widget.ttsController.stop();
|
||||
if (mounted) {
|
||||
setState(() => _isPlaying = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 180),
|
||||
child: Text(
|
||||
} else {
|
||||
if (mounted) {
|
||||
setState(() => _isPlaying = true);
|
||||
}
|
||||
try {
|
||||
await widget.ttsController.tryToSpeak(
|
||||
widget.text,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: _isPlaying
|
||||
? Theme.of(context).colorScheme.secondary
|
||||
: null,
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.titleLarge?.fontSize,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
context,
|
||||
targetID: 'text-audio-button',
|
||||
);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"text": widget.text,
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() => _isPlaying = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 180),
|
||||
child: Text(
|
||||
widget.text,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: _isPlaying
|
||||
? Theme.of(context).colorScheme.secondary
|
||||
: null,
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.titleLarge?.fontSize,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Icon(
|
||||
_isPlaying ? Icons.play_arrow : Icons.play_arrow_outlined,
|
||||
size: Theme.of(context).textTheme.titleLarge?.fontSize,
|
||||
),
|
||||
],
|
||||
const SizedBox(width: 4),
|
||||
Icon(
|
||||
_isPlaying ? Icons.play_arrow : Icons.play_arrow_outlined,
|
||||
size: Theme.of(context).textTheme.titleLarge?.fontSize,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -237,7 +237,6 @@ class WordZoomWidgetState extends State<WordZoomWidget> {
|
|||
WordTextWithAudioButton(
|
||||
text: widget.token.text.content,
|
||||
ttsController: widget.tts,
|
||||
eventID: widget.messageEvent.eventId,
|
||||
),
|
||||
// if _selectionType is null, we don't know if the lemma activity
|
||||
// can be shown yet, so we don't show the lemma definition
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue