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:
parent
87f60857e9
commit
592aa43089
16 changed files with 407 additions and 363 deletions
|
|
@ -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."
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,9 @@ class ChatInputRow extends StatelessWidget {
|
|||
}
|
||||
|
||||
return Column(
|
||||
// #Pangea
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
// Pangea#
|
||||
children: [
|
||||
// if (!controller.selectMode) WritingAssistanceInputRow(controller),
|
||||
CompositedTransformTarget(
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(() {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue