2421 reading assistance mode split feedback from will (#2422)

* chore: make input bar shorter in token mode

* chore: retry showing reading assistance content for initial token

* chore: make background lighter in token mode
This commit is contained in:
ggurdin 2025-04-11 11:28:56 -04:00 committed by GitHub
parent b7a6ee6fe2
commit a36a3417ba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 330 additions and 228 deletions

View file

@ -31,6 +31,17 @@ abstract class AppConfig {
static const double toolbarSpacing = 8.0;
static const double toolbarIconSize = 24.0;
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 =
readingAssistanceInputBarHeight +
toolbarButtonsHeight +
(chatInputRowOverlayPadding * 2) +
toolbarSpacing;
static TextStyle messageTextStyle(
Event? event,

View file

@ -1881,7 +1881,6 @@ class ChatController extends State<ChatPageWithRoom>
context: context,
child: overlayEntry!,
transformTargetId: "",
backgroundColor: Colors.black,
position: OverlayPositionEnum.centered,
onDismiss: clearSelectedEvents,
blurBackground: true,

View file

@ -24,7 +24,10 @@ class ReactionsPicker extends StatelessWidget {
return AnimatedContainer(
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
height: (display) ? 56 : 0,
// #Pangea
// height: (display) ? 56 : 0,
height: (display) ? AppConfig.reactionsPickerHeight : 0,
// Pangea#
child: Material(
color: Colors.transparent,
child: Builder(
@ -69,12 +72,19 @@ class ReactionsPicker extends StatelessWidget {
borderRadius: BorderRadius.circular(8),
onTap: () => controller.sendEmojiAction(emojis[i]),
child: Container(
width: 56,
height: 56,
// #Pangea
// width: 56,
// height: 56,
width: AppConfig.reactionsPickerHeight,
height: AppConfig.reactionsPickerHeight,
// Pangea#
alignment: Alignment.center,
child: Text(
emojis[i],
style: const TextStyle(fontSize: 30),
// #Pangea
// style: const TextStyle(fontSize: 30),
style: const TextStyle(fontSize: 20),
// Pangea#
),
),
),
@ -86,7 +96,10 @@ class ReactionsPicker extends StatelessWidget {
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 8),
width: 36,
height: 56,
// #Pangea
// height: 56,
height: AppConfig.reactionsPickerHeight,
// Pangea#
decoration: BoxDecoration(
color: theme.colorScheme.onInverseSurface,
shape: BoxShape.circle,

View file

@ -136,8 +136,11 @@ class PangeaChatInputRowState extends State<PangeaChatInputRow> {
CompositedTransformTarget(
link: _controller.choreographer.inputLayerLinkAndKey.link,
child: Container(
padding:
EdgeInsets.all(widget.overlayController != null ? 8.0 : 0.0),
padding: EdgeInsets.all(
widget.overlayController != null
? AppConfig.chatInputRowOverlayPadding
: 0.0,
),
decoration: BoxDecoration(
color: widget.overlayController != null
? Theme.of(context).cardColor

View file

@ -4,6 +4,7 @@ import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
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';
@ -11,11 +12,13 @@ class OverlayFooter extends StatelessWidget {
final ChatController controller;
final MessageOverlayController overlayController;
final bool showToolbarButtons;
final ReadingAssistanceMode? readingAssistanceMode;
const OverlayFooter({
required this.controller,
required this.overlayController,
required this.showToolbarButtons,
required this.readingAssistanceMode,
super.key,
});
@ -30,12 +33,10 @@ class OverlayFooter extends StatelessWidget {
left: bottomSheetPadding,
right: bottomSheetPadding,
),
// constraints: const BoxConstraints(
// maxWidth: FluffyThemes.columnWidth * 2.5,
// ),
height: AppConfig.readingAssistanceInputBarHeight +
AppConfig.toolbarButtonsHeight +
20.0,
height: readingAssistanceMode == ReadingAssistanceMode.messageMode ||
readingAssistanceMode == ReadingAssistanceMode.transitionMode
? AppConfig.messageModeInputBarHeight
: AppConfig.tokenModeInputBarHeight,
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,

View file

@ -205,7 +205,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
/// Decides whether an _initialSelectedToken should be used
/// for a first practice activity on the word meaning
void _initializeSelectedToken() {
Future<void> _initializeSelectedToken() async {
// if there is no initial selected token, then we don't need to do anything
if (widget._initialSelectedToken == null || practiceSelection == null) {
return;
@ -223,6 +223,17 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
}
_updateSelectedSpan(widget._initialSelectedToken!.text);
int retries = 0;
while (retries < 5 &&
selectedToken != null &&
!MatrixState.pAnyState.isOverlayOpen(
selectedToken!.text.uniqueKey,
)) {
await Future.delayed(const Duration(milliseconds: 100));
_showReadingAssistanceContent();
retries++;
}
}
/////////////////////////////////////

View file

@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
@ -68,6 +69,7 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
Animation<Offset>? _overlayOffsetAnimation;
Animation<Size>? _messageSizeAnimation;
Animation<double>? _blurAnimation;
Offset? _currentOffset;
StreamSubscription? _reactionSubscription;
@ -106,10 +108,19 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
},
).listen((_) => setState(() {}));
_blurAnimation = Tween<double>(begin: 0.0, end: 2.5).animate(
CurvedAnimation(
parent: _animationController,
curve: FluffyThemes.animationCurve,
),
);
WidgetsBinding.instance.addPostFrameCallback((_) async {
await _centeredMessageCompleter.future;
if (!mounted) return;
_animationController.forward(from: 0);
setState(() {
_currentOffset = Offset(
_ownMessage ? _messageRightOffset : _messageLeftOffset,
@ -152,7 +163,10 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
_mediaQuery!.size.height -
offset.dy -
renderBox.size.height -
_reactionsHeight,
_reactionsHeight +
((AppConfig.messageModeInputBarHeight -
AppConfig.tokenModeInputBarHeight) *
0.75),
);
setState(() {});
@ -254,9 +268,11 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
}
}
final double _inputBarSize = AppConfig.readingAssistanceInputBarHeight +
AppConfig.toolbarButtonsHeight +
20.0;
double get _inputBarSize =>
_readingAssistanceMode == ReadingAssistanceMode.messageMode ||
_readingAssistanceMode == ReadingAssistanceMode.transitionMode
? AppConfig.messageModeInputBarHeight
: AppConfig.tokenModeInputBarHeight;
bool get _showDetails =>
(Matrix.of(context).store.getBool(SettingKeys.displayChatDetailsColumn) ??
@ -487,6 +503,17 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
bool get _ownMessage =>
widget.event.senderId == widget.event.room.client.userID;
double get _readingAssistanceModeOpacity {
switch (_readingAssistanceMode) {
case ReadingAssistanceMode.messageMode:
case ReadingAssistanceMode.transitionMode:
return 0.8;
case ReadingAssistanceMode.tokenMode:
case null:
return 0.4;
}
}
@override
Widget build(BuildContext context) {
if (_messageRenderBox == null || _mediaQuery == null) {
@ -495,169 +522,204 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
widget.overlayController.maxWidth = _toolbarMaxWidth;
return Padding(
padding: EdgeInsets.only(
left: _horizontalPadding,
right: _horizontalPadding,
),
child: Stack(
alignment: Alignment.center,
children: [
Column(
children: [
Material(
type: MaterialType.transparency,
child: Column(
children: [
SizedBox(height: _mediaQuery?.padding.top ?? 0),
OverlayHeader(controller: widget.chatController),
],
),
),
const Expanded(
flex: 3,
child: SizedBox.shrink(),
),
Opacity(
opacity:
_readingAssistanceMode == ReadingAssistanceMode.messageMode
? 1.0
: 0.0,
child: OverlayCenterContent(
event: widget.event,
messageHeight: null,
messageWidth: null,
maxWidth: widget.overlayController.maxWidth,
overlayController: widget.overlayController,
chatController: widget.chatController,
pangeaMessageEvent: widget.pangeaMessageEvent,
nextEvent: widget.nextEvent,
prevEvent: widget.prevEvent,
hasReactions: _hasReactions,
onChangeMessageSize: _setCenteredMessageSize,
isTransitionAnimation: false,
maxHeight: _mediaQuery!.size.height -
_headerHeight -
_footerHeight -
AppConfig.toolbarSpacing * 2,
readingAssistanceMode: _readingAssistanceMode,
),
),
const Expanded(
flex: 1,
child: SizedBox.shrink(),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
OverlayFooter(
controller: widget.chatController,
overlayController: widget.overlayController,
showToolbarButtons: showToolbarButtons,
return Stack(
children: [
if (_blurAnimation != null)
IgnorePointer(
child: AnimatedOpacity(
duration: _animationDuration,
opacity: _readingAssistanceModeOpacity,
child: Positioned.fill(
child: DecoratedBox(
decoration: const BoxDecoration(
color: Colors.black,
),
child: AnimatedBuilder(
animation: _blurAnimation!,
builder: (context, _) {
return BackdropFilter(
filter: ImageFilter.blur(
sigmaX: _blurAnimation!.value,
sigmaY: _blurAnimation!.value,
),
SizedBox(height: _mediaQuery?.padding.bottom ?? 0),
child: Container(
height: double.infinity,
width: double.infinity,
color: Colors.transparent,
),
);
},
),
),
),
),
),
Padding(
padding: EdgeInsets.only(
left: _horizontalPadding,
right: _horizontalPadding,
),
child: Stack(
alignment: Alignment.center,
children: [
Column(
children: [
Material(
type: MaterialType.transparency,
child: Column(
children: [
SizedBox(height: _mediaQuery?.padding.top ?? 0),
OverlayHeader(controller: widget.chatController),
],
),
),
if (_showDetails)
const SizedBox(
width: FluffyThemes.columnWidth,
const Expanded(
flex: 3,
child: SizedBox.shrink(),
),
Opacity(
opacity: _readingAssistanceMode ==
ReadingAssistanceMode.messageMode
? 1.0
: 0.0,
child: OverlayCenterContent(
event: widget.event,
messageHeight: null,
messageWidth: null,
maxWidth: widget.overlayController.maxWidth,
overlayController: widget.overlayController,
chatController: widget.chatController,
pangeaMessageEvent: widget.pangeaMessageEvent,
nextEvent: widget.nextEvent,
prevEvent: widget.prevEvent,
hasReactions: _hasReactions,
onChangeMessageSize: _setCenteredMessageSize,
isTransitionAnimation: false,
maxHeight: _mediaQuery!.size.height -
_headerHeight -
_footerHeight -
AppConfig.toolbarSpacing * 2,
readingAssistanceMode: _readingAssistanceMode,
),
),
const Expanded(
flex: 1,
child: SizedBox.shrink(),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
OverlayFooter(
controller: widget.chatController,
overlayController: widget.overlayController,
showToolbarButtons: showToolbarButtons,
readingAssistanceMode: _readingAssistanceMode,
),
SizedBox(height: _mediaQuery?.padding.bottom ?? 0),
],
),
),
if (_showDetails)
const SizedBox(
width: FluffyThemes.columnWidth,
),
],
),
],
),
],
),
if (_readingAssistanceMode != ReadingAssistanceMode.messageMode)
AnimatedBuilder(
animation: _overlayOffsetAnimation ?? _animationController,
builder: (context, child) {
return Positioned(
left: _ownMessage
? null
: (_overlayOffsetAnimation?.value)?.dx ??
_messageLeftOffset,
right: _ownMessage
? (_overlayOffsetAnimation?.value)?.dx ??
_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,
),
);
},
),
if (showToolbarButtons)
Positioned(
top: 0,
child: IgnorePointer(
child: MeasureRenderBox(
onChange: _setTooltipSize,
child: Opacity(
opacity: 0.0,
child: Container(
constraints: BoxConstraints(
minWidth: 200.0,
maxWidth: _toolbarMaxWidth,
if (_readingAssistanceMode != ReadingAssistanceMode.messageMode)
AnimatedBuilder(
animation: _overlayOffsetAnimation ?? _animationController,
builder: (context, child) {
return Positioned(
left: _ownMessage
? null
: (_overlayOffsetAnimation?.value)?.dx ??
_messageLeftOffset,
right: _ownMessage
? (_overlayOffsetAnimation?.value)?.dx ??
_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,
),
child: InstructionsInlineTooltip(
instructionsEnum: widget.overlayController.toolbarMode
.instructionsEnum ??
InstructionsEnum.readingAssistanceOverview,
bold: true,
);
},
),
if (showToolbarButtons)
Positioned(
top: 0,
child: IgnorePointer(
child: MeasureRenderBox(
onChange: _setTooltipSize,
child: Opacity(
opacity: 0.0,
child: Container(
constraints: BoxConstraints(
minWidth: 200.0,
maxWidth: _toolbarMaxWidth,
),
child: InstructionsInlineTooltip(
instructionsEnum: widget.overlayController
.toolbarMode.instructionsEnum ??
InstructionsEnum.readingAssistanceOverview,
bold: true,
),
),
),
),
),
),
),
),
if (_centeredMessageTopOffset != null &&
_tooltipSize != null &&
widget.overlayController.toolbarMode !=
MessageMode.noneSelected &&
widget.overlayController.selectedToken == null)
Positioned(
top: max(
((_headerHeight + _centeredMessageTopOffset!) / 2) -
(_tooltipSize!.height / 2),
_headerHeight,
),
child: Container(
constraints: BoxConstraints(
minWidth: 200.0,
maxWidth: widget.overlayController.maxWidth,
),
child: InstructionsInlineTooltip(
instructionsEnum:
widget.overlayController.toolbarMode.instructionsEnum ??
if (_centeredMessageTopOffset != null &&
_tooltipSize != null &&
widget.overlayController.toolbarMode !=
MessageMode.noneSelected &&
widget.overlayController.selectedToken == null)
Positioned(
top: max(
((_headerHeight + _centeredMessageTopOffset!) / 2) -
(_tooltipSize!.height / 2),
_headerHeight,
),
child: Container(
constraints: BoxConstraints(
minWidth: 200.0,
maxWidth: widget.overlayController.maxWidth,
),
child: InstructionsInlineTooltip(
instructionsEnum: widget
.overlayController.toolbarMode.instructionsEnum ??
InstructionsEnum.readingAssistanceOverview,
bold: true,
bold: true,
),
),
),
),
),
],
),
],
),
),
],
);
}
}

View file

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
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';
@ -20,70 +19,73 @@ class ToolbarButtonRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox(
height: AppConfig.toolbarButtonsHeight,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
spacing: 4.0,
children: [
Container(
width: buttonSize + 4,
height: buttonSize + 4,
alignment: Alignment.center,
child: ToolbarButton(
mode: MessageMode.listening,
overlayController: overlayController,
onPressed: overlayController.updateToolbarMode,
buttonSize: buttonSize,
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
spacing: 4.0,
children: [
Container(
width: buttonSize + 4,
height: buttonSize + 4,
alignment: Alignment.center,
child: ToolbarButton(
mode: MessageMode.listening,
overlayController: overlayController,
onPressed: overlayController.updateToolbarMode,
buttonSize: buttonSize,
),
),
),
Container(
width: buttonSize + 4,
height: buttonSize + 4,
alignment: Alignment.center,
child: ToolbarButton(
mode: MessageMode.wordMorph,
overlayController: overlayController,
onPressed: overlayController.updateToolbarMode,
buttonSize: buttonSize,
Container(
width: buttonSize + 4,
height: buttonSize + 4,
alignment: Alignment.center,
child: ToolbarButton(
mode: MessageMode.wordMorph,
overlayController: overlayController,
onPressed: overlayController.updateToolbarMode,
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,
alignment: Alignment.center,
child: ToolbarButton(
mode: MessageMode.messageTranslation,
overlayController: overlayController,
onPressed: overlayController.updateToolbarMode,
buttonSize: buttonSize,
),
),
),
Container(
width: buttonSize + 4,
height: buttonSize + 4,
alignment: Alignment.center,
child: ToolbarButton(
mode: MessageMode.wordMeaning,
overlayController: overlayController,
onPressed: overlayController.updateToolbarMode,
buttonSize: buttonSize,
Container(
width: buttonSize + 4,
height: buttonSize + 4,
alignment: Alignment.center,
child: ToolbarButton(
mode: MessageMode.wordMeaning,
overlayController: overlayController,
onPressed: overlayController.updateToolbarMode,
buttonSize: buttonSize,
),
),
),
Container(
width: buttonSize + 4,
height: buttonSize + 4,
alignment: Alignment.center,
child: ToolbarButton(
mode: MessageMode.wordEmoji,
overlayController: overlayController,
onPressed: overlayController.updateToolbarMode,
buttonSize: buttonSize,
Container(
width: buttonSize + 4,
height: buttonSize + 4,
alignment: Alignment.center,
child: ToolbarButton(
mode: MessageMode.wordEmoji,
overlayController: overlayController,
onPressed: overlayController.updateToolbarMode,
buttonSize: buttonSize,
),
),
),
],
),
],
),
],
);
}
}