2507 still requesting unlocking translation faster (#2549)

* initial work for new reading assistance modes

* feat: added select mode buttons

* chore: highlight audio tokens, always open selection mode first
pull/1817/head
ggurdin 7 months ago committed by GitHub
parent 6a9e4350d1
commit 22f46caf80
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -33,11 +33,9 @@ abstract class AppConfig {
static const double readingAssistanceInputBarHeight = 140.0;
static const double reactionsPickerHeight = 48.0;
static const double chatInputRowOverlayPadding = 8.0;
static const double tokenModeInputBarHeight = reactionsPickerHeight +
toolbarButtonsHeight +
(chatInputRowOverlayPadding * 2) +
toolbarSpacing;
static const double messageModeInputBarHeight =
static const double selectModeInputBarHeight =
reactionsPickerHeight + (chatInputRowOverlayPadding * 2) + toolbarSpacing;
static const double practiceModeInputBarHeight =
readingAssistanceInputBarHeight +
toolbarButtonsHeight +
(chatInputRowOverlayPadding * 2) +

@ -1,9 +1,9 @@
enum ReadingAssistanceMode {
/// Overlay message is directly over the original message
tokenMode,
selectMode,
/// Overlay message is centered and larger than the original message
messageMode,
practiceMode,
/// Overlay message is moving to the center of the screen
transitionMode,

@ -6,7 +6,7 @@ import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pangea/chat/widgets/pangea_chat_input_row.dart';
import 'package:fluffychat/pangea/toolbar/enums/reading_assistance_mode_enum.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
import 'package:fluffychat/pangea/toolbar/widgets/toolbar_button_column.dart';
import 'package:fluffychat/pangea/toolbar/widgets/practice_mode_buttons.dart';
class OverlayFooter extends StatelessWidget {
final ChatController controller;
@ -33,16 +33,16 @@ class OverlayFooter extends StatelessWidget {
left: bottomSheetPadding,
right: bottomSheetPadding,
),
height: readingAssistanceMode == ReadingAssistanceMode.messageMode ||
height: readingAssistanceMode == ReadingAssistanceMode.practiceMode ||
readingAssistanceMode == ReadingAssistanceMode.transitionMode
? AppConfig.messageModeInputBarHeight
: AppConfig.tokenModeInputBarHeight,
? AppConfig.practiceModeInputBarHeight
: AppConfig.selectModeInputBarHeight,
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (showToolbarButtons)
ToolbarButtonRow(overlayController: overlayController),
PracticeModeButtons(overlayController: overlayController),
Material(
clipBehavior: Clip.hardEdge,
color: Colors.transparent,

@ -76,11 +76,11 @@ class TokenRenderingUtil {
}
switch (readingAssistanceMode!) {
case ReadingAssistanceMode.tokenMode:
case ReadingAssistanceMode.selectMode:
return isTransitionAnimation;
case ReadingAssistanceMode.transitionMode:
return false;
case ReadingAssistanceMode.messageMode:
case ReadingAssistanceMode.practiceMode:
return !isTransitionAnimation;
}
}

@ -90,6 +90,8 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
final GlobalKey<ReadingAssistanceContentState> wordZoomKey = GlobalKey();
ReadingAssistanceMode? readingAssistanceMode; // default mode
bool showTranslation = false;
String? translationText;
double maxWidth = AppConfig.toolbarMinWidth;
@ -289,7 +291,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
}
/// Update [selectedSpan]
void updateSelectedSpan(PangeaTokenText selectedSpan, [bool force = false]) {
void updateSelectedSpan(PangeaTokenText? selectedSpan, [bool force = false]) {
if (selectedMorph != null) {
selectedMorph = null;
}
@ -407,7 +409,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
pangeaMessageEvent!.event.messageType == MessageTypes.Text;
bool get hideWordCardContent =>
readingAssistanceMode == ReadingAssistanceMode.messageMode;
readingAssistanceMode == ReadingAssistanceMode.practiceMode;
bool get isPracticeComplete => isTranslationUnlocked;
@ -575,6 +577,18 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
}
}
void setShowTranslation(bool show, String? translation) {
if (showTranslation == show) return;
if (show && translation == null) return;
if (mounted) {
setState(() {
showTranslation = show;
translationText = show ? translation : null;
});
}
}
/////////////////////////////////////
/// Build
/////////////////////////////////////

@ -21,6 +21,7 @@ import 'package:fluffychat/pangea/toolbar/widgets/measure_render_box.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
import 'package:fluffychat/pangea/toolbar/widgets/overlay_center_content.dart';
import 'package:fluffychat/pangea/toolbar/widgets/overlay_header.dart';
import 'package:fluffychat/pangea/toolbar/widgets/select_mode_buttons.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart';
@ -112,14 +113,14 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
setState(() {
_currentOffset = Offset(
_ownMessage ? _messageRightOffset : _messageLeftOffset,
_originalMessageBottomOffset - _reactionsHeight,
_originalMessageBottomOffset -
_reactionsHeight -
_selectionButtonsHeight,
);
});
_setReadingAssistanceMode(
widget.initialSelectedToken == null
? ReadingAssistanceMode.messageMode
: ReadingAssistanceMode.tokenMode,
ReadingAssistanceMode.selectMode,
);
});
}
@ -129,9 +130,6 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
super.didUpdateWidget(oldWidget);
final mode = widget.overlayController.toolbarMode;
if (mode != _currentMode) {
if (_currentMode == MessageMode.noneSelected) {
_setReadingAssistanceMode(ReadingAssistanceMode.messageMode);
}
setState(() => _currentMode = mode);
}
}
@ -151,12 +149,12 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
_centeredMessageOffset = Offset(
offset.dx - _columnWidth - _horizontalPadding - 2.0,
_mediaQuery!.size.height -
offset.dy -
(offset.dy -
((AppConfig.practiceModeInputBarHeight -
AppConfig.selectModeInputBarHeight) *
0.75)) -
renderBox.size.height -
_reactionsHeight +
((AppConfig.messageModeInputBarHeight -
AppConfig.tokenModeInputBarHeight) *
0.75),
_reactionsHeight,
);
setState(() {});
@ -182,19 +180,19 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
await _centeredMessageCompleter.future;
if (mode == ReadingAssistanceMode.messageMode) {
if (mode == ReadingAssistanceMode.practiceMode) {
setState(
() => widget.overlayController.readingAssistanceMode =
ReadingAssistanceMode.transitionMode,
);
} else if (mode == ReadingAssistanceMode.tokenMode) {
} else if (mode == ReadingAssistanceMode.selectMode) {
setState(
() => widget.overlayController.readingAssistanceMode =
ReadingAssistanceMode.tokenMode,
ReadingAssistanceMode.selectMode,
);
}
if (mode == ReadingAssistanceMode.tokenMode) {
if (mode == ReadingAssistanceMode.selectMode) {
_overlayOffsetAnimation = Tween<Offset>(
begin: _currentOffset,
end: _adjustedOriginalMessageOffset,
@ -208,7 +206,7 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
setState(() => _currentOffset = _overlayOffsetAnimation?.value);
}
});
} else if (mode == ReadingAssistanceMode.messageMode) {
} else if (mode == ReadingAssistanceMode.practiceMode) {
_overlayOffsetAnimation = Tween<Offset>(
begin: _currentOffset,
end: _centeredMessageOffset!,
@ -266,10 +264,10 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
widget.overlayController.readingAssistanceMode;
double get _inputBarSize =>
_readingAssistanceMode == ReadingAssistanceMode.messageMode ||
_readingAssistanceMode == ReadingAssistanceMode.practiceMode ||
_readingAssistanceMode == ReadingAssistanceMode.transitionMode
? AppConfig.messageModeInputBarHeight
: AppConfig.tokenModeInputBarHeight;
? AppConfig.practiceModeInputBarHeight
: AppConfig.selectModeInputBarHeight;
bool get _showDetails =>
(Matrix.of(context).store.getBool(SettingKeys.displayChatDetailsColumn) ??
@ -399,7 +397,10 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
}
final topOffset = _originalMessageOffset.dy;
final bottomOffset = _originalMessageBottomOffset;
final bottomOffset = _originalMessageBottomOffset -
_reactionsHeight -
_selectionButtonsHeight;
final hasHeaderOverflow =
topOffset < (_headerHeight + AppConfig.toolbarSpacing);
final hasFooterOverflow =
@ -408,7 +409,7 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
if (!hasHeaderOverflow && !hasFooterOverflow) {
return Offset(
_ownMessage ? _messageRightOffset : _messageLeftOffset,
_originalMessageBottomOffset - _reactionsHeight,
bottomOffset,
);
}
@ -427,13 +428,9 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
newBottomOffset,
);
} else {
final difference =
bottomOffset - (_footerHeight + AppConfig.toolbarSpacing);
return Offset(
_ownMessage ? _messageRightOffset : _messageLeftOffset,
_mediaQuery!.size.height -
(_originalMessageOffset.dy + difference) -
_originalMessageSize.height,
_footerHeight + AppConfig.toolbarSpacing,
);
}
}
@ -487,10 +484,20 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
// measurement for items in the toolbar
bool get showToolbarButtons =>
bool get showPracticeButtons =>
(widget.pangeaMessageEvent?.shouldShowToolbar ?? false) &&
widget.pangeaMessageEvent?.event.messageType == MessageTypes.Text &&
(widget.pangeaMessageEvent?.messageDisplayLangIsL2 ?? false);
(widget.pangeaMessageEvent?.messageDisplayLangIsL2 ?? false) &&
widget.overlayController.readingAssistanceMode ==
ReadingAssistanceMode.practiceMode;
bool get showSelectionButtons =>
widget.overlayController.readingAssistanceMode ==
ReadingAssistanceMode.selectMode;
double get _selectionButtonsHeight {
return AppConfig.toolbarButtonsHeight;
}
bool get _hasReactions {
final reactionsEvents = widget.event.aggregatedEvents(
@ -507,10 +514,10 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
double get _readingAssistanceModeOpacity {
switch (_readingAssistanceMode) {
case ReadingAssistanceMode.messageMode:
case ReadingAssistanceMode.practiceMode:
case ReadingAssistanceMode.transitionMode:
return 0.8;
case ReadingAssistanceMode.tokenMode:
case ReadingAssistanceMode.selectMode:
case null:
return 0.4;
}
@ -564,7 +571,7 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
),
Opacity(
opacity: _readingAssistanceMode ==
ReadingAssistanceMode.messageMode
ReadingAssistanceMode.practiceMode
? 1.0
: 0.0,
child: OverlayCenterContent(
@ -601,7 +608,7 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
OverlayFooter(
controller: widget.chatController,
overlayController: widget.overlayController,
showToolbarButtons: showToolbarButtons,
showToolbarButtons: showPracticeButtons,
readingAssistanceMode: _readingAssistanceMode,
),
SizedBox(height: _mediaQuery?.padding.bottom ?? 0),
@ -616,7 +623,9 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
),
],
),
if (_readingAssistanceMode != ReadingAssistanceMode.messageMode)
if (_readingAssistanceMode !=
ReadingAssistanceMode.practiceMode &&
_readingAssistanceMode != null)
AnimatedBuilder(
animation: _overlayOffsetAnimation ?? _animationController,
builder: (context, child) {
@ -630,30 +639,50 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
_messageRightOffset
: null,
bottom: (_overlayOffsetAnimation?.value)?.dy ??
_originalMessageBottomOffset - _reactionsHeight,
child: OverlayCenterContent(
event: widget.event,
messageHeight: _originalMessageSize.height,
messageWidth: _originalMessageSize.width,
maxWidth: widget.overlayController.maxWidth,
overlayController: widget.overlayController,
chatController: widget.chatController,
pangeaMessageEvent: widget.pangeaMessageEvent,
nextEvent: widget.nextEvent,
prevEvent: widget.prevEvent,
hasReactions: _hasReactions,
sizeAnimation: _messageSizeAnimation,
isTransitionAnimation: true,
maxHeight: _mediaQuery!.size.height -
_headerHeight -
_footerHeight -
AppConfig.toolbarSpacing * 2,
readingAssistanceMode: _readingAssistanceMode,
_originalMessageBottomOffset -
_reactionsHeight -
_selectionButtonsHeight,
child: Column(
crossAxisAlignment: _ownMessage
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: [
OverlayCenterContent(
event: widget.event,
messageHeight: _originalMessageSize.height,
messageWidth: _originalMessageSize.width,
maxWidth: widget.overlayController.maxWidth,
overlayController: widget.overlayController,
chatController: widget.chatController,
pangeaMessageEvent: widget.pangeaMessageEvent,
nextEvent: widget.nextEvent,
prevEvent: widget.prevEvent,
hasReactions: _hasReactions,
sizeAnimation: _messageSizeAnimation,
isTransitionAnimation: true,
maxHeight: _mediaQuery!.size.height -
_headerHeight -
_footerHeight -
AppConfig.toolbarSpacing * 2,
readingAssistanceMode: _readingAssistanceMode,
),
if (showSelectionButtons)
SelectModeButtons(
overlayController: widget.overlayController,
lauchPractice: () {
_setReadingAssistanceMode(
ReadingAssistanceMode.practiceMode,
);
widget.overlayController
.updateSelectedSpan(null);
},
),
],
),
);
},
),
if (showToolbarButtons)
if (showPracticeButtons)
Positioned(
top: 0,
child: IgnorePointer(

@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pages/chat/events/message_reactions.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
@ -53,9 +55,11 @@ class OverlayCenterContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
final showTranslation = overlayController.showTranslation &&
overlayController.translationText != null;
return IgnorePointer(
ignoring: !isTransitionAnimation &&
readingAssistanceMode != ReadingAssistanceMode.messageMode,
readingAssistanceMode != ReadingAssistanceMode.practiceMode,
child: Container(
constraints: BoxConstraints(maxWidth: maxWidth),
child: Material(
@ -66,31 +70,80 @@ class OverlayCenterContent extends StatelessWidget {
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: [
MeasureRenderBox(
onChange: onChangeMessageSize,
child: OverlayMessage(
event,
pangeaMessageEvent: pangeaMessageEvent,
immersionMode: chatController.choreographer.immersionMode,
controller: chatController,
overlayController: overlayController,
nextEvent: nextEvent,
prevEvent: prevEvent,
timeline: chatController.timeline!,
sizeAnimation: sizeAnimation,
// there's a split seconds between when the transition animation starts and
// when the sizeAnimation is set when the original dimensions need to be enforced
messageWidth: (sizeAnimation == null && isTransitionAnimation)
? messageWidth
: null,
messageHeight:
(sizeAnimation == null && isTransitionAnimation)
? messageHeight
: null,
maxHeight: maxHeight,
isTransitionAnimation: isTransitionAnimation,
readingAssistanceMode: readingAssistanceMode,
),
Stack(
alignment: Alignment.topCenter,
children: [
if (overlayController.readingAssistanceMode ==
ReadingAssistanceMode.selectMode)
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
AppConfig.borderRadius,
),
color: Theme.of(context).colorScheme.primaryContainer,
),
padding: EdgeInsets.all(
showTranslation ? 8.0 : 0.0,
),
constraints: BoxConstraints(
maxWidth: messageWidth ?? maxWidth,
),
child: Column(
children: [
AnimatedContainer(
duration: FluffyThemes.animationDuration,
height: showTranslation ? messageHeight : 0,
width: showTranslation ? messageWidth : 0,
),
AnimatedSize(
duration: FluffyThemes.animationDuration,
child: SizedBox(
width: messageWidth,
child: showTranslation
? Text(
overlayController.translationText!,
style: AppConfig.messageTextStyle(
event,
Theme.of(context)
.colorScheme
.onPrimaryContainer,
),
textAlign: TextAlign.center,
)
: const SizedBox(),
),
),
],
),
),
MeasureRenderBox(
onChange: onChangeMessageSize,
child: OverlayMessage(
event,
pangeaMessageEvent: pangeaMessageEvent,
immersionMode: chatController.choreographer.immersionMode,
controller: chatController,
overlayController: overlayController,
nextEvent: nextEvent,
prevEvent: prevEvent,
timeline: chatController.timeline!,
sizeAnimation: sizeAnimation,
// there's a split seconds between when the transition animation starts and
// when the sizeAnimation is set when the original dimensions need to be enforced
messageWidth:
(sizeAnimation == null && isTransitionAnimation)
? messageWidth
: null,
messageHeight:
(sizeAnimation == null && isTransitionAnimation)
? messageHeight
: null,
maxHeight: maxHeight,
isTransitionAnimation: isTransitionAnimation,
readingAssistanceMode: readingAssistanceMode,
),
),
],
),
if (hasReactions)
Padding(

@ -4,10 +4,10 @@ import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
import 'package:fluffychat/pangea/toolbar/widgets/toolbar_button.dart';
class ToolbarButtonRow extends StatelessWidget {
class PracticeModeButtons extends StatelessWidget {
final MessageOverlayController overlayController;
const ToolbarButtonRow({
const PracticeModeButtons({
required this.overlayController,
super.key,
});
@ -48,17 +48,6 @@ class ToolbarButtonRow extends StatelessWidget {
buttonSize: buttonSize,
),
),
Container(
width: buttonSize + 4,
height: buttonSize + 4,
alignment: Alignment.center,
child: ToolbarButton(
mode: MessageMode.messageTranslation,
overlayController: overlayController,
onPressed: overlayController.updateToolbarMode,
buttonSize: buttonSize,
),
),
Container(
width: buttonSize + 4,
height: buttonSize + 4,

@ -0,0 +1,312 @@
import 'dart:async';
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:just_audio/just_audio.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat/events/audio_player.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/common/widgets/pressable_button.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart';
import 'package:fluffychat/pangea/events/models/representation_content_model.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_audio_card.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
import 'package:fluffychat/widgets/matrix.dart';
enum SelectMode {
audio(Icons.volume_up),
translate(Icons.translate),
practice(Symbols.fitness_center);
final IconData icon;
const SelectMode(this.icon);
String tooltip(BuildContext context) {
final l10n = L10n.of(context);
switch (this) {
case SelectMode.audio:
return l10n.playAudio;
case SelectMode.translate:
return l10n.translationTooltip;
case SelectMode.practice:
return l10n.practice;
}
}
}
class SelectModeButtons extends StatefulWidget {
final VoidCallback lauchPractice;
final MessageOverlayController overlayController;
const SelectModeButtons({
required this.lauchPractice,
required this.overlayController,
super.key,
});
@override
State<SelectModeButtons> createState() => SelectModeButtonsState();
}
class SelectModeButtonsState extends State<SelectModeButtons> {
static const double iconWidth = 36.0;
static const double buttonSize = 40.0;
SelectMode? _selectedMode;
final AudioPlayer _audioPlayer = AudioPlayer();
bool _isLoadingAudio = false;
PangeaAudioFile? _audioFile;
StreamSubscription? _onPlayerStateChanged;
StreamSubscription? _onAudioPositionChanged;
bool _isLoadingTranslation = false;
PangeaRepresentation? _repEvent;
@override
void initState() {
super.initState();
_onPlayerStateChanged = _audioPlayer.playerStateStream.listen((state) {
if (state.processingState == ProcessingState.completed) {
_audioPlayer.stop();
_audioPlayer.seek(null);
}
setState(() {});
});
_onAudioPositionChanged ??= _audioPlayer.positionStream.listen((state) {
if (_audioFile != null) {
widget.overlayController.highlightCurrentText(
state.inMilliseconds,
_audioFile!.tokens,
);
}
});
}
@override
void dispose() {
_audioPlayer.dispose();
_onPlayerStateChanged?.cancel();
_onAudioPositionChanged?.cancel();
super.dispose();
}
PangeaMessageEvent? get messageEvent =>
widget.overlayController.pangeaMessageEvent;
String? get l1Code =>
MatrixState.pangeaController.languageController.activeL1Code();
String? get l2Code =>
MatrixState.pangeaController.languageController.activeL2Code();
Future<void> _updateMode(SelectMode mode) async {
widget.overlayController.updateSelectedSpan(null);
if (_selectedMode == SelectMode.translate) {
widget.overlayController.setShowTranslation(false, null);
await Future.delayed(FluffyThemes.animationDuration);
}
setState(
() => _selectedMode =
_selectedMode == mode && mode != SelectMode.audio ? null : mode,
);
if (_selectedMode == SelectMode.audio) {
_playAudio();
return;
} else {
_audioPlayer.stop();
_audioPlayer.seek(null);
}
if (_selectedMode == SelectMode.practice) {
widget.lauchPractice();
return;
}
if (_selectedMode == SelectMode.translate) {
await _loadTranslation();
if (_repEvent == null) return;
widget.overlayController.setShowTranslation(
true,
_repEvent!.text,
);
}
}
Future<void> _fetchAudio() async {
if (!mounted || messageEvent == null) return;
setState(() => _isLoadingAudio = true);
try {
final String langCode = messageEvent!.messageDisplayLangCode;
final Event? localEvent = messageEvent!.getTextToSpeechLocal(
langCode,
messageEvent!.messageDisplayText,
);
if (localEvent != null) {
_audioFile = await localEvent.getPangeaAudioFile();
} else {
_audioFile = await messageEvent!.getMatrixAudioFile(
langCode,
);
}
if (mounted) setState(() => _isLoadingAudio = false);
} catch (e, s) {
debugger(when: kDebugMode);
ErrorHandler.logError(
e: e,
s: s,
m: 'something wrong getting audio in MessageAudioCardState',
data: {
'widget.messageEvent.messageDisplayLangCode':
messageEvent?.messageDisplayLangCode,
},
);
if (mounted) setState(() => _isLoadingAudio = false);
}
}
Future<void> _playAudio() async {
if (_audioPlayer.playerState.playing) {
await _audioPlayer.pause();
return;
} else if (_audioPlayer.position != Duration.zero) {
await _audioPlayer.play();
return;
}
if (_audioFile == null) {
await _fetchAudio();
}
if (_audioFile == null) return;
await _audioPlayer.setAudioSource(
BytesAudioSource(
_audioFile!.bytes,
_audioFile!.mimeType,
),
);
_audioPlayer.play();
}
Future<void> _fetchRepresentation() async {
if (l1Code == null || messageEvent == null || _repEvent != null) {
return;
}
_repEvent = messageEvent!.representationByLanguage(l1Code!)?.content;
if (_repEvent == null && mounted) {
_repEvent = await messageEvent?.representationByLanguageGlobal(
langCode: l1Code!,
);
}
}
Future<void> _loadTranslation() async {
if (!mounted) return;
setState(() => _isLoadingTranslation = true);
try {
await _fetchRepresentation();
} catch (err) {
ErrorHandler.logError(
e: err,
data: {},
);
}
if (mounted) {
setState(() => _isLoadingTranslation = false);
}
}
Widget icon(SelectMode mode) {
if (mode == SelectMode.audio) {
if (_isLoadingAudio) {
return const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator.adaptive(),
);
} else {
return Icon(
_audioPlayer.playerState.playing == true
? Icons.pause_outlined
: Icons.play_arrow,
size: 20,
color: mode == _selectedMode ? Colors.white : null,
);
}
}
if (mode == SelectMode.translate) {
if (_isLoadingTranslation) {
return const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator.adaptive(),
);
} else if (_repEvent != null) {
return Icon(
mode.icon,
size: 20,
color: mode == _selectedMode ? Colors.white : null,
);
}
}
return Icon(
mode.icon,
size: 20,
color: mode == _selectedMode ? Colors.white : null,
);
}
@override
Widget build(BuildContext context) {
return Container(
height: AppConfig.toolbarButtonsHeight,
alignment: Alignment.bottomCenter,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
spacing: 4.0,
children: [
for (final mode in SelectMode.values)
Tooltip(
message: mode.tooltip(context),
child: PressableButton(
depressed: mode == _selectedMode,
borderRadius: BorderRadius.circular(20),
color: Theme.of(context).colorScheme.primaryContainer,
onPressed: () => _updateMode(mode),
playSound: true,
child: Container(
height: buttonSize,
width: buttonSize,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
shape: BoxShape.circle,
),
child: icon(mode),
),
),
),
],
),
);
}
}

@ -127,7 +127,7 @@ class LemmaMeaningWidgetState extends State<LemmaMeaningWidget> {
widget.token!,
) &&
widget.controller!.readingAssistanceMode ==
ReadingAssistanceMode.messageMode) {
ReadingAssistanceMode.practiceMode) {
return WordZoomActivityButton(
icon: const Icon(Symbols.dictionary),
isSelected: widget.controller?.toolbarMode == MessageMode.wordMeaning,

Loading…
Cancel
Save