2202 positioning to dos in reading assistance (#2214)

* chore: move toolbar buttons above reading assistance input bar

* chore: positioning on message relative to header and footer / positioning of tooltip between message and header

* chore: update inline tooltip color

* chore: animate reading assistance input bar height
This commit is contained in:
ggurdin 2025-03-25 14:40:18 -04:00 committed by GitHub
parent 87f60857e9
commit 592aa43089
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 407 additions and 363 deletions

View file

@ -4831,5 +4831,7 @@
"referFriendDialogTitle": "Invite a friend to your conversation",
"referFriendDialogDesc": "Do you have a friend who is excited to learn a new language with you? Then copy and send this invitation link to join and start chatting with you today.",
"youUnlocked": "You've unlocked",
"resetInstructionTooltipsTitle": "Reset instruction tooltips",
"resetInstructionTooltipsDesc": "Click to show instruction tooltips like for a brand new user.",
"selectForGrammar": "Select a grammar icon for activities and details."
}

View file

@ -28,6 +28,7 @@ abstract class AppConfig {
static const double toolbarMinWidth = 350.0;
static const double defaultHeaderHeight = 56.0;
static const double readingAssistanceInputBarHeight = 170;
static const double toolbarButtonsHeight = 100.0;
static const double toolbarSpacing = 8.0;
static const double toolbarIconSize = 24.0;

View file

@ -75,6 +75,9 @@ class ChatInputRow extends StatelessWidget {
}
return Column(
// #Pangea
mainAxisSize: MainAxisSize.min,
// Pangea#
children: [
// if (!controller.selectMode) WritingAssistanceInputRow(controller),
CompositedTransformTarget(

View file

@ -382,7 +382,7 @@ class MessageContent extends StatelessWidget {
isSelected: overlayController != null ? isSelected : null,
messageMode: overlayController?.toolbarMode,
isHighlighted: (PangeaToken token) =>
overlayController!.toolbarMode.associatedActivityType !=
overlayController?.toolbarMode.associatedActivityType !=
null &&
overlayController?.messageAnalyticsEntry?.hasActivity(
overlayController!

View file

@ -86,7 +86,10 @@ class InstructionsInlineTooltipState extends State<InstructionsInlineTooltip>
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
color: AppConfig.gold.withAlpha(widget.bold ? 80 : 10),
color: Color.alphaBlend(
Colors.black.withAlpha(70),
AppConfig.gold,
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),

View file

@ -1,6 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
class ResetInstructionsListTile extends StatelessWidget {
const ResetInstructionsListTile({
@ -15,37 +18,21 @@ class ResetInstructionsListTile extends StatelessWidget {
//TODO: add to L10n
return ListTile(
leading: const Icon(Icons.lightbulb),
title: const Text(
"Reset instruction tooltips",
title: Text(
L10n.of(context).resetInstructionTooltipsTitle,
),
subtitle: const Text(
"Click to show instruction tooltips like for a brand new user.",
),
onTap: () => showAdaptiveDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text(
"Reset instruction tooltips?",
),
actions: [
TextButton(
onPressed: () {
controller.resetInstructionTooltips();
Navigator.of(context).pop();
},
child: const Text("Reset"),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("Cancel"),
),
],
);
},
subtitle: Text(
L10n.of(context).resetInstructionTooltipsDesc,
),
onTap: () async {
final resp = await showOkCancelAlertDialog(
context: context,
title: L10n.of(context).areYouSure,
);
if (resp == OkCancelResult.ok) {
controller.resetInstructionTooltips();
}
},
);
}
}

View file

@ -137,7 +137,10 @@ class SettingsLearningController extends State<SettingsLearning> {
debugPrint("Error resetting instruction tooltips: $e");
debugger(when: kDebugMode);
ErrorHandler.logError(
e: e, s: s, data: {"resetInstructionTooltips": true});
e: e,
s: s,
data: {"resetInstructionTooltips": true},
);
return null;
},
);

View file

@ -24,7 +24,8 @@ class DottedBorderPainter extends CustomPainter {
final path = Path()
..addRRect(
borderRadius.toRRect(Rect.fromLTWH(0, 0, size.width, size.height)));
borderRadius.toRRect(Rect.fromLTWH(0, 0, size.width, size.height)),
);
final dashPath = Path();
final pathMetrics = path.computeMetrics();

View file

@ -1,10 +1,13 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix_api_lite/model/message_types.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/chat_input_row.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
import 'package:fluffychat/pangea/toolbar/widgets/toolbar_button_column.dart';
class OverlayFooter extends StatelessWidget {
final ChatController controller;
@ -16,6 +19,12 @@ class OverlayFooter extends StatelessWidget {
super.key,
});
bool get showToolbarButtons =>
overlayController.pangeaMessageEvent != null &&
overlayController.pangeaMessageEvent!.shouldShowToolbar &&
overlayController.pangeaMessageEvent!.event.messageType ==
MessageTypes.Text;
@override
Widget build(BuildContext context) {
//@ggurdin can we change this mobile padding to 0? seems a some extrea space on mobile
@ -33,6 +42,10 @@ class OverlayFooter extends StatelessWidget {
alignment: Alignment.center,
child: Column(
children: [
ToolbarButtonRow(
overlayController: overlayController,
shouldShowToolbarButtons: showToolbarButtons,
),
Material(
clipBehavior: Clip.hardEdge,
color: Theme.of(context).colorScheme.surfaceContainerHighest,

View file

@ -110,17 +110,24 @@ class ReadingAssistanceInputBar extends StatelessWidget {
return Expanded(
child: Container(
height: AppConfig.readingAssistanceInputBarHeight,
padding: const EdgeInsets.all(8.0),
constraints: BoxConstraints(
maxHeight: (MediaQuery.of(context).size.height / 2) -
AppConfig.toolbarButtonsHeight,
),
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: const BorderRadius.all(
Radius.circular(8.0),
),
),
alignment: Alignment.center,
child: SingleChildScrollView(
child: barContent(context),
child: AnimatedSize(
duration: const Duration(
milliseconds: AppConfig.overlayAnimationDuration,
),
child: SingleChildScrollView(
child: barContent(context),
),
),
),
);

View file

@ -38,6 +38,14 @@ class MeasureRenderBoxState extends State<MeasureRenderBox> {
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) => _updateOffset());
return widget.child;
return NotificationListener<SizeChangedLayoutNotification>(
onNotification: (notification) {
WidgetsBinding.instance.addPostFrameCallback((_) => _updateOffset());
return true;
},
child: SizeChangedLayoutNotifier(
child: widget.child,
),
);
}
}

View file

@ -290,20 +290,22 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
pangeaMessageEvent: pangeaMessageEvent!,
overlayController: this,
);
OverlayUtil.showPositionedCard(
context: context,
cardToShow: entry,
transformTargetId: selectedToken!.text.uniqueKey,
closePrevOverlay: false,
backDropToDismiss: false,
addBorder: false,
overlayKey: selectedToken!.text.uniqueKey,
maxHeight: AppConfig.toolbarMaxHeight,
maxWidth: AppConfig.toolbarMinWidth,
);
if (context.mounted) {
OverlayUtil.showPositionedCard(
context: context,
cardToShow: entry,
transformTargetId: selectedToken!.text.uniqueKey,
closePrevOverlay: false,
backDropToDismiss: false,
addBorder: false,
overlayKey: selectedToken!.text.uniqueKey,
maxHeight: AppConfig.toolbarMaxHeight,
maxWidth: AppConfig.toolbarMinWidth,
);
}
}
setState(() {});
if (mounted) setState(() {});
}
void updateToolbarMode(MessageMode mode) => setState(() {

View file

@ -13,13 +13,14 @@ import 'package:fluffychat/pangea/common/utils/error_handler.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/pangea_token_model.dart';
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart';
import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart';
import 'package:fluffychat/pangea/toolbar/reading_assistance_input_row/overlay_footer.dart';
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/toolbar_button_column.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart';
@ -54,21 +55,18 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
with TickerProviderStateMixin {
late AnimationController _animationController;
Animation<Offset>? _overlayOffsetAnimation;
Animation<Offset>? _buttonsOffsetAnimation;
Animation<Size>? _messageSizeAnimation;
StreamSubscription? _reactionSubscription;
/// if the message height is too tall to fit with the tools, adjust the message height
double? _adjustedMessageHeight;
Offset? _centeredMessageOffset;
Size? _centeredMessageSize;
final Completer _centeredMessageCompleter = Completer();
Offset? _centeredButtonsOffset;
Size? _centeredButtonsSize;
final Completer _centeredButtonsCompleter = Completer();
Size? _tooltipSize;
Size? _inputBarSize;
final Completer _centeredMessageCompleter = Completer();
final Completer _tooltipCompleter = Completer();
bool _finishedAnimation = false;
@ -105,7 +103,7 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
Future.wait([
_centeredMessageCompleter.future,
_centeredButtonsCompleter.future,
_tooltipCompleter.future,
]).then((_) => _startAnimation());
}
@ -131,7 +129,6 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
}
void _setCenteredMessageSize(RenderBox renderBox) {
if (_finishedAnimation) return;
_centeredMessageSize = renderBox.size;
final offset = renderBox.localToGlobal(Offset.zero);
_centeredMessageOffset = Offset(
@ -148,58 +145,32 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
}
}
void _setCenteredButtonsSize(RenderBox renderBox) {
if (_finishedAnimation) return;
_centeredButtonsSize = renderBox.size;
final offset = renderBox.localToGlobal(Offset.zero);
_centeredButtonsOffset = Offset(
offset.dx - _columnWidth - _horizontalPadding - 2.0,
offset.dy,
);
setState(() {});
void _setTooltipSize(RenderBox renderBox) {
setState(() {
_tooltipSize = renderBox.size;
});
if (!_centeredButtonsCompleter.isCompleted) {
_centeredButtonsCompleter.complete();
if (!_tooltipCompleter.isCompleted) {
_tooltipCompleter.complete();
}
}
void _setInputBarSize(RenderBox renderBox) {
setState(() => _inputBarSize = renderBox.size);
}
void _startAnimation() {
if (_mediaQuery == null) {
return;
}
// if the message height is too tall to fit, adjust the message height
if (_totalVerticalSpace! < _maxTotalToolbarHeight) {
_adjustedMessageHeight = _totalVerticalSpace! -
// one for within the toolbar itself, one for the top, and one for the bottom
((AppConfig.toolbarSpacing * 3) +
_reactionsHeight +
AppConfig.toolbarMaxHeight);
_adjustedMessageHeight = max(_adjustedMessageHeight!, 0);
}
_overlayOffsetAnimation = Tween<Offset>(
begin: Offset(
_ownMessage ? _messageRightOffset : _messageLeftOffset,
_totalToolbarBottomOffset,
_messageBottomOffset - _reactionsHeight,
),
// For own messages, dx is the right offset. For other's messages, dx is the left offset.
end: _centeredMessageOffset,
).animate(
CurvedAnimation(
parent: _animationController,
curve: FluffyThemes.animationCurve,
),
);
_buttonsOffsetAnimation = Tween<Offset>(
begin: Offset(
_ownMessage
? (_centeredButtonsSize!.width * -1)
: _centeredButtonsSize!.width + _mediaQuery!.size.width,
_totalToolbarBottomOffset,
),
end: _centeredButtonsOffset,
end: _adjustedCenteredMessageOffset,
).animate(
CurvedAnimation(
parent: _animationController,
@ -210,9 +181,9 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
_messageSizeAnimation = Tween<Size>(
begin: Size(
_messageSize.width,
_messageHeight,
_originalMessageHeight,
),
end: _centeredMessageSize,
end: _adjustedCenteredMessageSize,
).animate(
CurvedAnimation(
parent: _animationController,
@ -220,16 +191,6 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
),
);
// _contentSizeAnimation = Tween<double>(
// begin: 0,
// end: 1,
// ).animate(
// CurvedAnimation(
// parent: _animationController,
// curve: FluffyThemes.animationCurve,
// ),
// );
_animationController.forward().then((_) {
_finishedAnimation = true;
if (mounted) setState(() {});
@ -296,7 +257,43 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
);
}
double get _messageHeight => _adjustedMessageHeight ?? _messageSize.height;
double get _originalMessageHeight => _messageSize.height;
double? get _centerSpace {
if (_mediaQuery == null) return null;
return _mediaQuery!.size.height - _headerHeight - _footerHeight;
}
bool get _centeredMessageHasOverflow {
if (_centerSpace == null ||
_centeredMessageSize == null ||
_centeredMessageOffset == null) {
return false;
}
final finalMessageHeight = _centeredMessageSize!.height + _reactionsHeight;
return finalMessageHeight > _centerSpace!;
}
Size? get _adjustedCenteredMessageSize {
if (_centeredMessageHasOverflow) {
return Size(
_centeredMessageSize!.width,
_centerSpace! - (AppConfig.toolbarSpacing * 2),
);
}
return _centeredMessageSize;
}
Offset? get _adjustedCenteredMessageOffset {
if (_centeredMessageHasOverflow) {
return Offset(
_centeredMessageOffset!.dx,
_footerHeight + AppConfig.toolbarSpacing,
);
}
return _centeredMessageOffset;
}
//TODO: figure out where the 16 and 8 come from and use references instead of hard-coded values
static const _messageDefaultLeftMargin = Avatar.defaultSize + 16 + 8;
@ -338,7 +335,19 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
}
double get _messageBottomOffset =>
_mediaQuery!.size.height - _messageOffset.dy - _messageHeight;
_mediaQuery!.size.height - _messageOffset.dy - _originalMessageHeight;
double? get _centeredMessageTopOffset {
if (_mediaQuery == null ||
_adjustedCenteredMessageOffset == null ||
_adjustedCenteredMessageSize == null) {
return null;
}
return _mediaQuery!.size.height -
_adjustedCenteredMessageOffset!.dy -
_adjustedCenteredMessageSize!.height -
_reactionsHeight;
}
double get _messageLeftOffset => max(
_messageOffset.dx - _columnWidth - _horizontalPadding,
@ -367,18 +376,11 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
}
double get _footerHeight {
return AppConfig.readingAssistanceInputBarHeight +
(FluffyThemes.isColumnMode(context) ? 16.0 : 8.0) +
return (_inputBarSize?.height ??
(showToolbarButtons ? AppConfig.toolbarButtonsHeight : 0)) +
(_mediaQuery?.padding.bottom ?? 0);
}
double? get _totalVerticalSpace {
if (_mediaQuery == null) {
return null;
}
return _mediaQuery!.size.height - _headerHeight - _footerHeight;
}
// measurement for items in the toolbar
bool get showToolbarButtons =>
@ -396,17 +398,6 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
double get _reactionsHeight => _hasReactions ? 28 : 0;
double get _maxTotalCenterHeight =>
_reactionsHeight +
_messageHeight +
AppConfig.toolbarSpacing +
AppConfig.toolbarMaxHeight;
double get _maxTotalToolbarHeight => _maxTotalCenterHeight;
double get _totalToolbarBottomOffset =>
_messageBottomOffset - _reactionsHeight;
bool get _ownMessage =>
widget.event.senderId == widget.event.room.client.userID;
@ -424,24 +415,75 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
child: Stack(
alignment: Alignment.center,
children: [
Opacity(
opacity: _finishedAnimation ? 1.0 : 0.0,
child: OverlayCenterContent(
event: widget.event,
messageHeight: _messageHeight,
messageWidth: _messageSize.width,
toolbarMaxWidth: _toolbarMaxWidth,
overlayController: widget.overlayController,
chatController: widget.chatController,
pangeaMessageEvent: widget.pangeaMessageEvent,
nextEvent: widget.nextEvent,
prevEvent: widget.prevEvent,
showToolbarButtons: showToolbarButtons,
hasReactions: _hasReactions,
onChangeMessageSize: _setCenteredMessageSize,
onChangeButtonsSize: _setCenteredButtonsSize,
isTransitionAnimation: false,
),
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: _finishedAnimation ? 1.0 : 0.0,
child: OverlayCenterContent(
event: widget.event,
messageHeight: null,
messageWidth: null,
// messageHeight: _adjustedCenteredMessageSize?.height,
// messageWidth: _adjustedCenteredMessageSize?.width,
maxWidth: _toolbarMaxWidth,
overlayController: widget.overlayController,
chatController: widget.chatController,
pangeaMessageEvent: widget.pangeaMessageEvent,
nextEvent: widget.nextEvent,
prevEvent: widget.prevEvent,
showToolbarButtons: showToolbarButtons,
hasReactions: _hasReactions,
onChangeMessageSize: _setCenteredMessageSize,
isTransitionAnimation: false,
transitionAnimationFinished: _finishedAnimation,
maxHeight: _mediaQuery!.size.height -
_headerHeight -
_footerHeight -
AppConfig.toolbarSpacing * 2,
),
),
const Expanded(
flex: 1,
child: SizedBox.shrink(),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
MeasureRenderBox(
onChange: _setInputBarSize,
child: OverlayFooter(
controller: widget.chatController,
overlayController: widget.overlayController,
),
),
SizedBox(height: _mediaQuery?.padding.bottom ?? 0),
],
),
),
if (_showDetails)
const SizedBox(
width: FluffyThemes.columnWidth,
),
],
),
],
),
if (!_finishedAnimation)
AnimatedBuilder(
@ -457,12 +499,12 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
_messageRightOffset
: null,
bottom: (_overlayOffsetAnimation?.value)?.dy ??
_totalToolbarBottomOffset,
_messageBottomOffset - _reactionsHeight,
child: OverlayCenterContent(
event: widget.event,
messageHeight: _messageHeight,
messageHeight: _originalMessageHeight,
messageWidth: _messageSize.width,
toolbarMaxWidth: _toolbarMaxWidth,
maxWidth: _toolbarMaxWidth,
overlayController: widget.overlayController,
chatController: widget.chatController,
pangeaMessageEvent: widget.pangeaMessageEvent,
@ -472,75 +514,57 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
hasReactions: _hasReactions,
sizeAnimation: _messageSizeAnimation,
isTransitionAnimation: true,
transitionAnimationFinished: _finishedAnimation,
maxHeight: _mediaQuery!.size.height -
_headerHeight -
_footerHeight -
AppConfig.toolbarSpacing * 2,
),
);
},
),
if (!_finishedAnimation && _buttonsOffsetAnimation != null)
AnimatedBuilder(
animation: _buttonsOffsetAnimation!,
builder: (context, child) {
return Positioned(
left: (_buttonsOffsetAnimation?.value)!.dx,
top: _centeredButtonsOffset?.dy,
child: ToolbarButtonRow(
event: widget.event,
overlayController: widget.overlayController,
shouldShowToolbarButtons: showToolbarButtons,
MeasureRenderBox(
onChange: _setTooltipSize,
child: Positioned(
top: 0,
left: 0,
child: Opacity(
opacity: 0.0,
child: Container(
constraints: const BoxConstraints(
minWidth: 200.0,
maxWidth: 400.0,
),
);
},
),
Align(
alignment: Alignment.bottomCenter,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// ToolbarButtonRow(
// event: widget.overlayController.pangeaMessageEvent!.event,
// overlayController: widget.overlayController,
// shouldShowToolbarButtons: showToolbarButtons,
// ),
OverlayFooter(
controller: widget.chatController,
overlayController: widget.overlayController,
),
SizedBox(height: _mediaQuery?.padding.bottom ?? 0),
],
child: InstructionsInlineTooltip(
instructionsEnum:
widget.overlayController.toolbarMode.instructionsEnum ??
InstructionsEnum.readingAssistanceOverview,
bold: true,
),
),
if (_showDetails)
const SizedBox(
width: FluffyThemes.columnWidth,
),
],
),
),
),
Material(
type: MaterialType.transparency,
child: Column(
children: [
SizedBox(height: _mediaQuery?.padding.top ?? 0),
OverlayHeader(controller: widget.chatController),
widget.overlayController.toolbarMode.instructionsEnum != null
? Container(
constraints: const BoxConstraints(
maxWidth: 400,
),
child: InstructionsInlineTooltip(
instructionsEnum: widget
.overlayController.toolbarMode.instructionsEnum!,
bold: true,
),
)
: const SizedBox.shrink(),
],
if (_centeredMessageTopOffset != null && _finishedAnimation)
Positioned(
top: max(
((_headerHeight + _centeredMessageTopOffset!) / 2) -
(_tooltipSize!.height / 2),
_headerHeight,
),
child: Container(
constraints: const BoxConstraints(
minWidth: 200.0,
maxWidth: 400.0,
),
child: InstructionsInlineTooltip(
instructionsEnum:
widget.overlayController.toolbarMode.instructionsEnum ??
InstructionsEnum.readingAssistanceOverview,
bold: true,
),
),
),
),
],
),
);

View file

@ -8,7 +8,6 @@ import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dar
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_message.dart';
import 'package:fluffychat/pangea/toolbar/widgets/toolbar_button_column.dart';
class OverlayCenterContent extends StatelessWidget {
final Event event;
@ -21,22 +20,24 @@ class OverlayCenterContent extends StatelessWidget {
final Animation<Size>? sizeAnimation;
final void Function(RenderBox)? onChangeMessageSize;
final void Function(RenderBox)? onChangeContentSize;
final void Function(RenderBox)? onChangeButtonsSize;
final double messageHeight;
final double messageWidth;
final double toolbarMaxWidth;
final double? messageHeight;
final double? messageWidth;
final double maxWidth;
final double maxHeight;
final bool showToolbarButtons;
final bool hasReactions;
final bool isTransitionAnimation;
final bool transitionAnimationFinished;
const OverlayCenterContent({
required this.event,
required this.messageHeight,
required this.messageWidth,
required this.toolbarMaxWidth,
required this.maxWidth,
required this.maxHeight,
required this.overlayController,
required this.chatController,
required this.pangeaMessageEvent,
@ -45,78 +46,62 @@ class OverlayCenterContent extends StatelessWidget {
required this.showToolbarButtons,
required this.hasReactions,
this.onChangeMessageSize,
this.onChangeContentSize,
this.onChangeButtonsSize,
this.sizeAnimation,
this.isTransitionAnimation = false,
this.transitionAnimationFinished = false,
super.key,
});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
constraints: BoxConstraints(maxWidth: toolbarMaxWidth),
child: Material(
type: MaterialType.transparency,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: event.senderId == event.room.client.userID
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: [
MeasureRenderBox(
onChange: onChangeMessageSize,
child: OverlayMessage(
return Container(
constraints: BoxConstraints(maxWidth: maxWidth),
child: Material(
type: MaterialType.transparency,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: event.senderId == event.room.client.userID
? 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,
),
),
if (hasReactions)
Padding(
padding: const EdgeInsets.all(4),
child: SizedBox(
height: 20,
child: MessageReactions(
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,
isTransitionAnimation: isTransitionAnimation,
chatController.timeline!,
),
),
if (hasReactions)
Padding(
padding: const EdgeInsets.all(4),
child: SizedBox(
height: 20,
child: MessageReactions(
event,
chatController.timeline!,
),
),
),
],
),
),
),
],
),
if (!isTransitionAnimation)
MeasureRenderBox(
onChange: onChangeButtonsSize,
child: ToolbarButtonRow(
event: event,
overlayController: overlayController,
shouldShowToolbarButtons: showToolbarButtons,
),
),
],
),
);
}
}

View file

@ -27,6 +27,7 @@ class OverlayMessage extends StatelessWidget {
final Animation<Size>? sizeAnimation;
final double? messageWidth;
final double? messageHeight;
final double maxHeight;
final bool isTransitionAnimation;
@ -38,6 +39,7 @@ class OverlayMessage extends StatelessWidget {
required this.timeline,
required this.messageWidth,
required this.messageHeight,
required this.maxHeight,
this.pangeaMessageEvent,
this.nextEvent,
this.prevEvent,
@ -119,22 +121,25 @@ class OverlayMessage extends StatelessWidget {
? ThemeData.dark().colorScheme.onPrimary
: theme.colorScheme.onSurface;
final content = SingleChildScrollView(
dragStartBehavior: DragStartBehavior.down,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
AppConfig.borderRadius,
),
final content = Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
AppConfig.borderRadius,
),
padding: noBubble || noPadding
? EdgeInsets.zero
: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
width: messageWidth,
height: messageHeight,
),
padding: noBubble || noPadding
? EdgeInsets.zero
: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
width: messageWidth,
height: messageHeight,
constraints: BoxConstraints(
maxHeight: maxHeight,
),
child: SingleChildScrollView(
dragStartBehavior: DragStartBehavior.down,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,

View file

@ -2,17 +2,16 @@ import 'package:flutter/material.dart';
import 'package:matrix/matrix.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';
class ToolbarButtonRow extends StatelessWidget {
final Event event;
final MessageOverlayController overlayController;
final bool shouldShowToolbarButtons;
const ToolbarButtonRow({
required this.event,
required this.overlayController,
required this.shouldShowToolbarButtons,
super.key,
@ -25,84 +24,85 @@ class ToolbarButtonRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (event.messageType == MessageTypes.Audio ||
if (overlayController.event.messageType == MessageTypes.Audio ||
!shouldShowToolbarButtons ||
!(overlayController.pangeaMessageEvent?.messageDisplayLangIsL2 ??
false)) {
return const SizedBox(
height: 50.0,
height: AppConfig.toolbarButtonsHeight,
);
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
ToolbarButton(
mode: MessageMode.messageTranslation,
overlayController: overlayController,
onPressed: overlayController.updateToolbarMode,
buttonSize: buttonSize,
),
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
// wrapping these with a container to prevent the buttons from
// moving around when they press and depress
Container(
width: buttonSize + 4,
height: buttonSize + 4,
alignment: Alignment.center,
child: ToolbarButton(
mode: MessageMode.wordMorph,
return SizedBox(
height: AppConfig.toolbarButtonsHeight,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
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,
],
),
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.wordMorph,
overlayController: overlayController,
onPressed: overlayController.updateToolbarMode,
buttonSize: buttonSize,
),
),
),
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.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.listening,
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,
),
),
],
),
],
),
);
}
}