diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart
index 5b4dce09b..43d9e59e3 100644
--- a/lib/pages/chat/events/html_message.dart
+++ b/lib/pages/chat/events/html_message.dart
@@ -11,7 +11,6 @@ import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pages/chat/chat.dart';
-import 'package:fluffychat/pangea/common/utils/any_state_holder.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/message_token_text/message_token_button.dart';
@@ -21,7 +20,6 @@ import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart
import 'package:fluffychat/utils/event_checkbox_extension.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
-import 'package:fluffychat/widgets/matrix.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
import '../../../utils/url_launcher.dart';
@@ -412,68 +410,57 @@ class HtmlMessage extends StatelessWidget {
alignment: readingAssistanceMode == ReadingAssistanceMode.practiceMode
? PlaceholderAlignment.bottom
: PlaceholderAlignment.middle,
- child: CompositedTransformTarget(
- link: token != null && renderer.assignTokenKey
- ? MatrixState.pAnyState
- .layerLinkAndKey(token.text.uniqueKey)
- .link
- : LayerLinkAndKey(token.hashCode.toString()).link,
- child: Column(
- key: token != null && renderer.assignTokenKey
- ? MatrixState.pAnyState
- .layerLinkAndKey(token.text.uniqueKey)
- .key
- : null,
- children: [
- if (renderer.showCenterStyling && token != null)
- MessageTokenButton(
- token: token,
- overlayController: overlayController,
- textStyle: renderer.style(
+ child: Column(
+ children: [
+ if (renderer.showCenterStyling && token != null)
+ MessageTokenButton(
+ token: token,
+ overlayController: overlayController,
+ textStyle: renderer.style(
+ context,
+ color: renderer.backgroundColor(
context,
- color: renderer.backgroundColor(
- context,
- selected,
- highlighted,
- isNew,
- ),
+ selected,
+ highlighted,
+ isNew,
),
- width: tokenWidth,
- animateIn: isTransitionAnimation,
),
- MouseRegion(
- cursor: SystemMouseCursors.click,
- child: GestureDetector(
- behavior: HitTestBehavior.translucent,
- onTap: onClick != null && token != null
- ? () => onClick?.call(token)
- : null,
- child: RichText(
- textDirection: pangeaMessageEvent?.textDirection,
- text: TextSpan(
- children: [
- LinkifySpan(
- text: node.text,
- style: renderer.style(
+ width: tokenWidth,
+ animateIn: isTransitionAnimation,
+ ),
+ MouseRegion(
+ cursor: SystemMouseCursors.click,
+ child: GestureDetector(
+ behavior: HitTestBehavior.translucent,
+ onTap: onClick != null && token != null
+ ? () => onClick?.call(token)
+ : null,
+ child: RichText(
+ textDirection: pangeaMessageEvent?.textDirection,
+ text: TextSpan(
+ children: [
+ LinkifySpan(
+ text: node.text,
+ style: renderer.style(
+ context,
+ color: renderer.backgroundColor(
context,
- color: renderer.backgroundColor(
- context,
- selected,
- highlighted,
- isNew,
- ),
+ selected,
+ highlighted,
+ isNew,
),
- linkStyle: linkStyle,
- onOpen: (url) =>
- UrlLauncher(context, url.url).launchUrl(),
),
- ],
- ),
+ linkStyle: linkStyle,
+ onOpen: (url) =>
+ UrlLauncher(context, url.url).launchUrl(),
+ ),
+ ],
),
),
),
- ],
- ),
+ ),
+ ],
+ // ),
),
);
// Pangea#
diff --git a/lib/pangea/toolbar/reading_assistance_input_row/overlay_footer.dart b/lib/pangea/toolbar/reading_assistance_input_row/overlay_footer.dart
deleted file mode 100644
index 3511f23ac..000000000
--- a/lib/pangea/toolbar/reading_assistance_input_row/overlay_footer.dart
+++ /dev/null
@@ -1,60 +0,0 @@
-import 'package:flutter/material.dart';
-
-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/practice_mode_buttons.dart';
-
-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,
- });
-
- @override
- Widget build(BuildContext context) {
- //@ggurdin can we change this mobile padding to 0? seems a some extrea space on mobile
- final bottomSheetPadding = FluffyThemes.isColumnMode(context) ? 16.0 : 8.0;
-
- return Container(
- margin: EdgeInsets.only(
- bottom: bottomSheetPadding,
- left: bottomSheetPadding,
- right: bottomSheetPadding,
- ),
- height: readingAssistanceMode == ReadingAssistanceMode.practiceMode ||
- readingAssistanceMode == ReadingAssistanceMode.transitionMode
- ? AppConfig.practiceModeInputBarHeight
- : AppConfig.selectModeInputBarHeight,
- alignment: Alignment.center,
- child: Column(
- mainAxisAlignment: MainAxisAlignment.end,
- children: [
- if (showToolbarButtons)
- PracticeModeButtons(overlayController: overlayController),
- Material(
- clipBehavior: Clip.hardEdge,
- color: Colors.transparent,
- borderRadius: const BorderRadius.all(
- Radius.circular(AppConfig.borderRadius),
- ),
- child: PangeaChatInputRow(
- controller: controller,
- ),
- ),
- ],
- ),
- );
- }
-}
diff --git a/lib/pangea/toolbar/reading_assistance_input_row/reading_assistance_input_bar.dart b/lib/pangea/toolbar/reading_assistance_input_row/reading_assistance_input_bar.dart
index df9211e68..a597ea356 100644
--- a/lib/pangea/toolbar/reading_assistance_input_row/reading_assistance_input_bar.dart
+++ b/lib/pangea/toolbar/reading_assistance_input_row/reading_assistance_input_bar.dart
@@ -4,11 +4,11 @@ import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart';
-import 'package:fluffychat/pangea/toolbar/enums/reading_assistance_mode_enum.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_mode_locked_card.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_translation_card.dart';
import 'package:fluffychat/pangea/toolbar/widgets/practice_activity/practice_activity_card.dart';
+import 'package:fluffychat/pangea/toolbar/widgets/practice_mode_buttons.dart';
const double minContentHeight = 120;
@@ -23,12 +23,6 @@ class ReadingAssistanceInputBar extends StatelessWidget {
});
Widget barContent(BuildContext context) {
- if (overlayController.readingAssistanceMode !=
- ReadingAssistanceMode.practiceMode) {
- return const SizedBox();
- // return ReactionsPicker(controller);
- }
-
Widget? content;
final target =
overlayController.toolbarMode.associatedActivityType != null &&
@@ -57,6 +51,7 @@ class ReadingAssistanceInputBar extends StatelessWidget {
.textTheme
.bodyLarge
?.copyWith(fontStyle: FontStyle.italic),
+ textAlign: TextAlign.center,
);
case MessageMode.messageTranslation:
@@ -78,7 +73,10 @@ class ReadingAssistanceInputBar extends StatelessWidget {
overlayController: overlayController,
);
} else {
- content = Text(L10n.of(context).allDone);
+ content = Text(
+ L10n.of(context).allDone,
+ textAlign: TextAlign.center,
+ );
}
case MessageMode.wordMorph:
if (target != null) {
@@ -92,37 +90,43 @@ class ReadingAssistanceInputBar extends StatelessWidget {
child: Text(
L10n.of(context).selectForGrammar,
style: Theme.of(context).textTheme.bodyLarge,
+ textAlign: TextAlign.center,
),
);
}
}
}
- return Container(
- constraints: const BoxConstraints(
- minHeight: minContentHeight,
- ),
- child: Center(child: content),
- );
+ return content;
}
@override
Widget build(BuildContext context) {
- return Expanded(
- child: ConstrainedBox(
- constraints: BoxConstraints(
- maxHeight: AppConfig.readingAssistanceInputBarHeight,
- maxWidth: overlayController.maxWidth,
+ return Column(
+ children: [
+ PracticeModeButtons(
+ overlayController: overlayController,
),
- child: AnimatedSize(
- duration: const Duration(
- milliseconds: AppConfig.overlayAnimationDuration,
- ),
- child: SingleChildScrollView(
- child: barContent(context),
+ Material(
+ child: Container(
+ padding: const EdgeInsets.all(8.0),
+ alignment: Alignment.center,
+ constraints: BoxConstraints(
+ minHeight: minContentHeight,
+ maxHeight: AppConfig.readingAssistanceInputBarHeight,
+ maxWidth: overlayController.maxWidth,
+ ),
+ child: AnimatedSize(
+ duration: const Duration(
+ milliseconds: AppConfig.overlayAnimationDuration,
+ ),
+ child: SingleChildScrollView(
+ child: barContent(context),
+ ),
+ ),
),
),
- ),
+ ],
);
}
}
diff --git a/lib/pangea/toolbar/utils/token_rendering_util.dart b/lib/pangea/toolbar/utils/token_rendering_util.dart
index 6e4304b10..20fd7225d 100644
--- a/lib/pangea/toolbar/utils/token_rendering_util.dart
+++ b/lib/pangea/toolbar/utils/token_rendering_util.dart
@@ -25,7 +25,7 @@ class TokenRenderingUtil {
bool get showCenterStyling {
if (overlayController == null) return false;
if (!isTransitionAnimation) return true;
- return readingAssistanceMode == ReadingAssistanceMode.transitionMode;
+ return readingAssistanceMode == ReadingAssistanceMode.practiceMode;
}
double? fontSize(BuildContext context) => showCenterStyling
diff --git a/lib/pangea/toolbar/widgets/icon_number_widget.dart b/lib/pangea/toolbar/widgets/icon_number_widget.dart
deleted file mode 100644
index 099baf3df..000000000
--- a/lib/pangea/toolbar/widgets/icon_number_widget.dart
+++ /dev/null
@@ -1,52 +0,0 @@
-import 'package:flutter/material.dart';
-
-class IconNumberWidget extends StatelessWidget {
- final IconData icon;
- final String number;
- final Color? iconColor;
- final double? iconSize;
- final String? toolTip;
- final VoidCallback? onPressed;
-
- const IconNumberWidget({
- super.key,
- required this.icon,
- required this.number,
- this.toolTip,
- this.iconColor,
- this.iconSize,
- this.onPressed,
- });
-
- Widget _content(BuildContext context) {
- return Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- IconButton(
- icon: Icon(
- icon,
- color: iconColor ?? Theme.of(context).iconTheme.color,
- size: iconSize ?? Theme.of(context).iconTheme.size,
- ),
- onPressed: onPressed,
- ),
- const SizedBox(width: 5),
- Text(
- number.toString(),
- style: TextStyle(
- fontSize:
- iconSize ?? Theme.of(context).textTheme.bodyMedium?.fontSize,
- color: iconColor ?? Theme.of(context).textTheme.bodyMedium?.color,
- ),
- ),
- ],
- );
- }
-
- @override
- Widget build(BuildContext context) {
- return toolTip != null
- ? Tooltip(message: toolTip!, child: _content(context))
- : _content(context);
- }
-}
diff --git a/lib/pangea/toolbar/widgets/message_selection_positioner.dart b/lib/pangea/toolbar/widgets/message_selection_positioner.dart
index 948109db4..12c728d48 100644
--- a/lib/pangea/toolbar/widgets/message_selection_positioner.dart
+++ b/lib/pangea/toolbar/widgets/message_selection_positioner.dart
@@ -14,10 +14,12 @@ import 'package:fluffychat/pages/chat/chat.dart';
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/models/pangea_token_model.dart';
+import 'package:fluffychat/pangea/toolbar/enums/reading_assistance_mode_enum.dart';
+import 'package:fluffychat/pangea/toolbar/reading_assistance_input_row/reading_assistance_input_bar.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/reading_assistance_content.dart';
-import 'package:fluffychat/pangea/toolbar/widgets/select_mode_buttons.dart';
+import 'package:fluffychat/pangea/toolbar/widgets/over_message_overlay.dart';
+import 'package:fluffychat/pangea/toolbar/widgets/practice_mode_transition_animation.dart';
+import 'package:fluffychat/pangea/toolbar/widgets/word_card_switcher.dart';
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart';
@@ -51,57 +53,32 @@ class MessageSelectionPositioner extends StatefulWidget {
class MessageSelectionPositionerState extends State
with TickerProviderStateMixin {
- // late AnimationController _animationController;
-
- // Offset? _centeredMessageOffset;
- // Size? _centeredMessageSize;
-
- // Size? _tooltipSize;
-
- // final Completer _centeredMessageCompleter = Completer();
- // final Completer _tooltipCompleter = Completer();
-
- // MessageMode _currentMode = MessageMode.noneSelected;
-
- // Animation? _overlayOffsetAnimation;
- // Animation? _messageSizeAnimation;
- // Offset? _currentOffset;
-
StreamSubscription? _reactionSubscription;
StreamSubscription? _contentChangedSubscription;
- ScrollController? _scrollController;
+ ScrollController? scrollController;
- // final _animationDuration = const Duration(
- // milliseconds: AppConfig.overlayAnimationDuration,
- // // seconds: 5,
- // );
+ bool finishedTransition = false;
+ bool startedTransition = false;
+
+ ReadingAssistanceMode readingAssistanceMode =
+ ReadingAssistanceMode.selectMode;
@override
void initState() {
super.initState();
- _scrollController = ScrollController();
- Future.delayed(
- const Duration(milliseconds: 100),
- () {
- if (_scrollController == null || !_scrollController!.hasClients) {
- return;
- }
-
- // _scrollController!.animateTo(
- // _scrollController!.position.maxScrollExtent,
- // duration: FluffyThemes.animationDuration,
- // curve: FluffyThemes.animationCurve,
- // );
+ scrollController = ScrollController(
+ onAttach: (position) {
+ Future.delayed(const Duration(milliseconds: 200), () {
+ if (mounted) {
+ scrollController?.jumpTo(
+ scrollController!.position.maxScrollExtent,
+ );
+ }
+ });
},
);
- // // _currentMode = widget.overlayController.toolbarMode;
- // _animationController = AnimationController(
- // vsync: this,
- // duration: _animationDuration,
- // );
-
_reactionSubscription =
widget.chatController.room.client.onSync.stream.where(
(update) {
@@ -125,360 +102,19 @@ class MessageSelectionPositionerState extends State
_contentChangedSubscription = widget
.overlayController.contentChangedStream.stream
.listen(_onContentSizeChanged);
-
- // WidgetsBinding.instance.addPostFrameCallback((_) async {
- // await _centeredMessageCompleter.future;
- // if (!mounted) return;
-
- // setState(() {
- // _currentOffset = Offset(
- // _ownMessage ? _messageRightOffset : _messageLeftOffset,
- // _originalMessageBottomOffset -
- // _reactionsHeight -
- // _selectionButtonsHeight,
- // );
- // });
-
- // _setReadingAssistanceMode(
- // ReadingAssistanceMode.selectMode,
- // );
- // });
}
- // @override
- // void didUpdateWidget(MessageSelectionPositioner oldWidget) {
- // super.didUpdateWidget(oldWidget);
- // final mode = widget.overlayController.toolbarMode;
- // if (mode != _currentMode) {
- // setState(() => _currentMode = mode);
- // }
- // }
-
@override
void dispose() {
- // _animationController.dispose();
_reactionSubscription?.cancel();
_contentChangedSubscription?.cancel();
- _scrollController?.dispose();
+ scrollController?.dispose();
MatrixState.pangeaController.matrixState.audioPlayer
?..stop()
..dispose();
super.dispose();
}
- // void _setCenteredMessageSize(RenderBox renderBox) {
- // if (_centeredMessageCompleter.isCompleted) return;
-
- // _centeredMessageSize = renderBox.size;
- // final offset = renderBox.localToGlobal(Offset.zero);
- // _centeredMessageOffset = Offset(
- // offset.dx - _columnWidth - _horizontalPadding - 2.0,
- // _mediaQuery!.size.height -
- // (offset.dy -
- // ((AppConfig.practiceModeInputBarHeight -
- // AppConfig.selectModeInputBarHeight) *
- // 0.75)) -
- // renderBox.size.height -
- // _reactionsHeight,
- // );
- // setState(() {});
-
- // if (!_centeredMessageCompleter.isCompleted) {
- // _centeredMessageCompleter.complete();
- // }
- // }
-
- // void _setTooltipSize(RenderBox renderBox) {
- // setState(() {
- // _tooltipSize = renderBox.size;
- // });
-
- // if (!_tooltipCompleter.isCompleted) {
- // _tooltipCompleter.complete();
- // }
- // }
-
- // Future _setReadingAssistanceMode(ReadingAssistanceMode mode) async {
- // if (mode == _readingAssistanceMode) {
- // return;
- // }
-
- // await _centeredMessageCompleter.future;
-
- // if (mode == ReadingAssistanceMode.practiceMode) {
- // setState(
- // () => widget.overlayController.readingAssistanceMode =
- // ReadingAssistanceMode.transitionMode,
- // );
- // } else if (mode == ReadingAssistanceMode.selectMode) {
- // setState(
- // () => widget.overlayController.readingAssistanceMode =
- // ReadingAssistanceMode.selectMode,
- // );
- // }
-
- // if (mode == ReadingAssistanceMode.selectMode) {
- // _resetOffsetAnimation(_adjustedOriginalMessageOffset);
- // } else if (mode == ReadingAssistanceMode.practiceMode) {
- // _resetOffsetAnimation(_centeredMessageOffset!);
- // _messageSizeAnimation = Tween(
- // begin: Size(
- // _originalMessageSize.width,
- // _originalMessageSize.height,
- // ),
- // end: _adjustedCenteredMessageSize,
- // ).animate(
- // CurvedAnimation(
- // parent: _animationController,
- // curve: FluffyThemes.animationCurve,
- // ),
- // );
- // }
-
- // await _animationController.forward(from: 0);
- // if (mounted) {
- // setState(() => widget.overlayController.readingAssistanceMode = mode);
- // }
- // }
-
- void _onContentSizeChanged(_) {
- Future.delayed(FluffyThemes.animationDuration, () {
- setState(() {});
- // final offset = _overlayMessageRenderBox?.localToGlobal(Offset.zero);
- // if (offset == null || !_overlayMessageRenderBox!.hasSize) {
- // return null;
- // }
-
- // final newOffset = _adjustedMessageOffset(
- // _overlayMessageRenderBox!.size,
- // offset,
- // );
-
- // if (newOffset == _currentOffset) return;
- // _resetOffsetAnimation(newOffset);
- // _animationController.forward(from: 0);
- });
- }
-
- // void _resetOffsetAnimation(Offset offset) {
- // _overlayOffsetAnimation = Tween(
- // begin: _currentOffset,
- // end: offset,
- // ).animate(
- // CurvedAnimation(
- // parent: _animationController,
- // curve: FluffyThemes.animationCurve,
- // ),
- // )..addListener(() {
- // if (mounted) {
- // setState(() => _currentOffset = _overlayOffsetAnimation?.value);
- // }
- // });
- // }
-
- // double get _inputBarSize =>
- // _readingAssistanceMode == ReadingAssistanceMode.practiceMode ||
- // _readingAssistanceMode == ReadingAssistanceMode.transitionMode
- // ? AppConfig.practiceModeInputBarHeight
- // : AppConfig.selectModeInputBarHeight;
-
- // /// Available vertical space not taken up by the header and footer
- // double? get _verticalSpace {
- // if (_mediaQuery == null) return null;
- // return _mediaQuery!.size.height - _headerHeight - _footerHeight;
- // }
-
- // original message size and offset
-
- // Offset? get _overlayMessageOffset =>
- // _overlayMessageRenderBox?.localToGlobal(Offset.zero);
-
- // double? get _buttonsTopOffset {
- // if (_overlayMessageOffset == null ||
- // _overlayMessageSize == null ||
- // _mediaQuery == null) {
- // return null;
- // }
-
- // const buttonsHeight = 300.0;
- // final availableSpace = _mediaQuery!.size.height -
- // _overlayMessageOffset!.dy -
- // _overlayMessageSize!.height -
- // _reactionsHeight -
- // 4.0;
-
- // if (availableSpace >= buttonsHeight) {
- // return _overlayMessageOffset!.dy + _overlayMessageSize!.height + 4.0;
- // }
-
- // return _mediaQuery!.size.height - buttonsHeight - 4.0;
- // }
-
- // Centered message size and offset
-
- // bool get _centeredMessageHasOverflow {
- // if (_verticalSpace == null ||
- // _centeredMessageSize == null ||
- // _centeredMessageOffset == null) {
- // return false;
- // }
-
- // final finalMessageHeight = _centeredMessageSize!.height + _reactionsHeight;
- // return finalMessageHeight > _verticalSpace!;
- // }
-
- // /// Size of the centered overlay message adjusted for overflow
- // Size? get _adjustedCenteredMessageSize {
- // if (_centeredMessageHasOverflow) {
- // return Size(
- // _centeredMessageSize!.width,
- // _verticalSpace! - (AppConfig.toolbarSpacing * 2),
- // );
- // }
- // return _centeredMessageSize;
- // }
-
- // Offset? get _adjustedCenteredMessageOffset {
- // if (_centeredMessageHasOverflow) {
- // return Offset(
- // _centeredMessageOffset!.dx,
- // _footerHeight + AppConfig.toolbarSpacing,
- // );
- // }
- // return _centeredMessageOffset;
- // }
-
- // message offset
-
- // Offset get _adjustedOriginalMessageOffset {
- // return _adjustedMessageOffset(
- // _originalMessageSize,
- // _originalMessageOffset,
- // );
- // }
-
- // Offset _adjustedMessageOffset(
- // Size messageSize,
- // Offset messageOffset,
- // ) {
- // if (_messageRenderBox == null || !_messageRenderBox!.hasSize) {
- // return _defaultMessageOffset;
- // }
-
- // final topOffset = messageOffset.dy;
- // final bottomOffset =
- // (_mediaQuery!.size.height - topOffset - messageSize.height) -
- // _reactionsHeight -
- // _selectionButtonsHeight;
-
- // final hasHeaderOverflow =
- // topOffset < (_headerHeight + AppConfig.toolbarSpacing);
- // final hasFooterOverflow =
- // bottomOffset < (_footerHeight + AppConfig.toolbarSpacing);
-
- // if (!hasHeaderOverflow && !hasFooterOverflow) {
- // return Offset(
- // _ownMessage ? _messageRightOffset : _messageLeftOffset,
- // bottomOffset,
- // );
- // }
-
- // if (hasHeaderOverflow) {
- // final difference = topOffset - (_headerHeight + AppConfig.toolbarSpacing);
-
- // double newBottomOffset = _mediaQuery!.size.height -
- // topOffset +
- // difference -
- // messageSize.height -
- // _selectionButtonsHeight;
-
- // if (newBottomOffset < _footerHeight + AppConfig.toolbarSpacing) {
- // newBottomOffset = _footerHeight + AppConfig.toolbarSpacing;
- // }
-
- // return Offset(
- // _ownMessage ? _messageRightOffset : _messageLeftOffset,
- // newBottomOffset,
- // );
- // } else {
- // return Offset(
- // _ownMessage ? _messageRightOffset : _messageLeftOffset,
- // _footerHeight + (AppConfig.toolbarSpacing * 2),
- // );
- // }
- // }
-
- // double get _originalMessageBottomOffset =>
- // _mediaQuery!.size.height -
- // _originalMessageOffset.dy -
- // _originalMessageSize.height;
-
- // double? get _centeredMessageTopOffset {
- // if (_mediaQuery == null ||
- // _adjustedCenteredMessageOffset == null ||
- // _adjustedCenteredMessageSize == null) {
- // return null;
- // }
- // return _mediaQuery!.size.height -
- // _adjustedCenteredMessageOffset!.dy -
- // _adjustedCenteredMessageSize!.height -
- // _reactionsHeight;
- // }
-
- // double get _headerHeight {
- // return (Theme.of(context).appBarTheme.toolbarHeight ??
- // AppConfig.defaultHeaderHeight) +
- // (_mediaQuery?.padding.top ?? 0);
- // }
-
- // double get _footerHeight {
- // return _inputBarSize + (_mediaQuery?.padding.bottom ?? 0);
- // }
-
- // measurement for items in the toolbar
-
- // bool get _showButtons {
- // if (!(widget.pangeaMessageEvent?.shouldShowToolbar ?? false)) {
- // return false;
- // }
-
- // final type = widget.pangeaMessageEvent?.event.messageType;
- // if (![MessageTypes.Text, MessageTypes.Audio].contains(type)) {
- // return false;
- // }
-
- // if (type == MessageTypes.Text) {
- // return widget.pangeaMessageEvent?.messageDisplayLangIsL2 ?? false;
- // }
-
- // return true;
- // }
-
- // bool get showPracticeButtons =>
- // _showButtons &&
- // widget.overlayController.readingAssistanceMode ==
- // ReadingAssistanceMode.practiceMode;
-
- // bool get showSelectionButtons =>
- // _showButtons &&
- // [ReadingAssistanceMode.selectMode, null]
- // .contains(widget.overlayController.readingAssistanceMode);
-
- // double get _selectionButtonsHeight {
- // return showSelectionButtons ? AppConfig.toolbarButtonsHeight : 0;
- // }
-
- // double get _readingAssistanceModeOpacity {
- // switch (_readingAssistanceMode) {
- // case ReadingAssistanceMode.practiceMode:
- // case ReadingAssistanceMode.transitionMode:
- // return 0.8;
- // case ReadingAssistanceMode.selectMode:
- // case null:
- // return 0.6;
- // }
- // }
-
T _runWithLogging(
Function runner,
String errorMessage,
@@ -498,10 +134,16 @@ class MessageSelectionPositionerState extends State
}
}
+ final Duration transitionAnimationDuration =
+ const Duration(milliseconds: 300);
+
+ final Offset _defaultMessageOffset =
+ const Offset(Avatar.defaultSize + 16 + 8, 300);
+
double get _horizontalPadding =>
FluffyThemes.isColumnMode(context) ? 8.0 : 0.0;
- bool get _hasReactions {
+ bool get hasReactions {
final reactionsEvents = widget.event.aggregatedEvents(
widget.chatController.timeline!,
RelationshipTypes.reaction,
@@ -509,23 +151,23 @@ class MessageSelectionPositionerState extends State
return reactionsEvents.where((e) => !e.redacted).isNotEmpty;
}
- double get _reactionsHeight => _hasReactions ? 32.0 : 0.0;
+ double get reactionsHeight => hasReactions ? 32.0 : 0.0;
- bool get _ownMessage =>
+ bool get ownMessage =>
widget.event.senderId == widget.event.room.client.userID;
- bool get _showDetails =>
+ bool get showDetails =>
AppSettings.displayChatDetailsColumn.getItem(Matrix.of(context).store) &&
FluffyThemes.isThreeColumnMode(context) &&
widget.chatController.room.membership == Membership.join;
- MediaQueryData? get _mediaQuery => _runWithLogging(
+ MediaQueryData? get mediaQuery => _runWithLogging(
() => MediaQuery.of(context),
"Error getting media query",
null,
);
- double get _columnWidth => FluffyThemes.isColumnMode(context)
+ double get columnWidth => FluffyThemes.isColumnMode(context)
? (FluffyThemes.columnWidth + FluffyThemes.navRailWidth + 1.0)
: 0;
@@ -538,8 +180,8 @@ class MessageSelectionPositionerState extends State
messageMargin;
double? maxWidth;
- if (_mediaQuery != null) {
- final chatViewWidth = _mediaQuery!.size.width - _columnWidth;
+ if (mediaQuery != null) {
+ final chatViewWidth = mediaQuery!.size.width - columnWidth;
maxWidth = chatViewWidth - (2 * _horizontalPadding) - messageMargin;
}
@@ -550,9 +192,6 @@ class MessageSelectionPositionerState extends State
return maxWidth;
}
- static const Offset _defaultMessageOffset =
- Offset(Avatar.defaultSize + 16 + 8, 300);
-
Size get _defaultMessageSize => const Size(FluffyThemes.columnWidth / 2, 100);
RenderBox? get _overlayMessageRenderBox => _runWithLogging(
@@ -565,6 +204,18 @@ class MessageSelectionPositionerState extends State
Size? get _overlayMessageSize => _overlayMessageRenderBox?.size;
+ Offset? get overlayMessageOffset {
+ if (_overlayMessageRenderBox == null ||
+ !_overlayMessageRenderBox!.hasSize) {
+ return null;
+ }
+ return _runWithLogging(
+ () => _overlayMessageRenderBox?.localToGlobal(Offset.zero),
+ "Error getting overlay message offset",
+ null,
+ );
+ }
+
RenderBox? get _messageRenderBox => _runWithLogging(
() => MatrixState.pAnyState.getRenderBox(
widget.event.eventId,
@@ -585,7 +236,7 @@ class MessageSelectionPositionerState extends State
}
/// The size of the message in the chat list (as opposed to the expanded size in the center overlay)
- Size get _originalMessageSize {
+ Size get originalMessageSize {
if (_messageRenderBox == null || !_messageRenderBox!.hasSize) {
return _defaultMessageSize;
}
@@ -597,25 +248,23 @@ class MessageSelectionPositionerState extends State
);
}
- double? get _messageLeftOffset {
- if (_ownMessage) return null;
- return max(_originalMessageOffset.dx - _columnWidth, 0);
+ double? get messageLeftOffset {
+ if (ownMessage) return null;
+ return max(_originalMessageOffset.dx - columnWidth, 0);
}
- double? get _messageRightOffset {
- if (_mediaQuery == null || !_ownMessage) return null;
- return _mediaQuery!.size.width -
+ double? get messageRightOffset {
+ if (mediaQuery == null || !ownMessage) return null;
+ return mediaQuery!.size.width -
_originalMessageOffset.dx -
- _originalMessageSize.width -
- (_showDetails ? FluffyThemes.columnWidth : 0);
+ originalMessageSize.width -
+ (showDetails ? FluffyThemes.columnWidth : 0);
}
- double? get _contentHeight {
- if (_overlayMessageSize == null) return null;
- return _overlayMessageSize!.height +
- _reactionsHeight +
- AppConfig.toolbarMenuHeight +
- 4.0;
+ double get _contentHeight {
+ final messageHeight =
+ _overlayMessageSize?.height ?? originalMessageSize.height;
+ return messageHeight + reactionsHeight + AppConfig.toolbarMenuHeight + 4.0;
}
double get _overheadContentHeight {
@@ -625,44 +274,86 @@ class MessageSelectionPositionerState extends State
: 40.0;
}
- double? get _availableSpaceAboveContent {
- if (_contentHeight == null || _mediaQuery == null) return null;
- return max(
- 0,
- (_mediaQuery!.size.height -
- _mediaQuery!.padding.top -
- _mediaQuery!.padding.bottom -
- _contentHeight!) /
- 2,
- );
- }
-
- double? get _wordCardTopOffset {
- if (_contentHeight == null || _availableSpaceAboveContent == null) {
- return null;
- }
-
- if (_availableSpaceAboveContent! >= _overheadContentHeight) {
- return _availableSpaceAboveContent! - _overheadContentHeight - 4.0;
- }
-
- return 0;
- }
-
double? get _wordCardLeftOffset {
- if (_ownMessage) return null;
+ if (ownMessage) return null;
if (widget.pangeaMessageEvent != null &&
widget.overlayController.selectedToken != null &&
- _mediaQuery != null &&
- (_mediaQuery!.size.width < _toolbarMaxWidth + _messageLeftOffset!)) {
- return _mediaQuery!.size.width - _toolbarMaxWidth - 8.0;
+ mediaQuery != null &&
+ (mediaQuery!.size.width < _toolbarMaxWidth + messageLeftOffset!)) {
+ return mediaQuery!.size.width - _toolbarMaxWidth - 8.0;
+ }
+ return messageLeftOffset;
+ }
+
+ double get _fullContentHeight {
+ return _contentHeight + _overheadContentHeight;
+ }
+
+ bool get shouldScroll {
+ if (mediaQuery == null) return false;
+ return _fullContentHeight >
+ (mediaQuery!.size.height -
+ mediaQuery!.padding.bottom -
+ mediaQuery!.padding.top);
+ }
+
+ bool get _hasFooterOverflow {
+ if (mediaQuery == null || _overlayMessageSize == null) return false;
+ final bottomOffset = _originalMessageOffset.dy +
+ originalMessageSize.height +
+ reactionsHeight +
+ AppConfig.toolbarMenuHeight +
+ 4.0;
+ return bottomOffset >
+ (mediaQuery!.size.height -
+ mediaQuery!.padding.bottom -
+ mediaQuery!.padding.top);
+ }
+
+ double get spaceAboveContent {
+ if (shouldScroll) return _overheadContentHeight;
+ if (_hasFooterOverflow) {
+ return mediaQuery!.size.height -
+ mediaQuery!.padding.top -
+ _fullContentHeight;
+ }
+
+ return _originalMessageOffset.dy -
+ mediaQuery!.padding.top -
+ _overheadContentHeight;
+ }
+
+ void _onContentSizeChanged(_) {
+ Future.delayed(FluffyThemes.animationDuration, () {
+ setState(() {});
+ });
+ }
+
+ void onStartedTransition() {
+ if (mounted) {
+ setState(() {
+ startedTransition = true;
+ });
+ }
+ }
+
+ void onFinishedTransition() {
+ if (mounted) {
+ setState(() {
+ finishedTransition = true;
+ });
+ }
+ }
+
+ void setReadingAssistanceMode(ReadingAssistanceMode mode) {
+ if (mounted) {
+ setState(() => readingAssistanceMode = mode);
}
- return _messageLeftOffset;
}
@override
Widget build(BuildContext context) {
- if (_messageRenderBox == null || _mediaQuery == null) {
+ if (_messageRenderBox == null || mediaQuery == null) {
return const SizedBox.shrink();
}
@@ -672,119 +363,55 @@ class MessageSelectionPositionerState extends State
children: [
Column(
children: [
- Expanded(
- child: SizedBox(
- width: _mediaQuery!.size.width -
- _columnWidth -
- (_showDetails ? FluffyThemes.columnWidth : 0),
- child: Stack(
- alignment: _ownMessage
- ? Alignment.centerRight
- : Alignment.centerLeft,
- children: [
- GestureDetector(
- onTap: widget.chatController.clearSelectedEvents,
- child: SingleChildScrollView(
- controller: _scrollController,
- padding: EdgeInsets.only(
- left: _messageLeftOffset ?? 0.0,
- right: _messageRightOffset ?? 0.0,
- ),
- child: Column(
- crossAxisAlignment: _ownMessage
- ? CrossAxisAlignment.end
- : CrossAxisAlignment.start,
- mainAxisSize: MainAxisSize.min,
- children: [
- if (_contentHeight != null &&
- _mediaQuery != null &&
- _availableSpaceAboveContent != null &&
- _availableSpaceAboveContent! <
- _overheadContentHeight)
- AnimatedContainer(
- duration: FluffyThemes.animationDuration,
- height: _contentHeight! +
- _overheadContentHeight >
- _mediaQuery!.size.height
- ? _overheadContentHeight
- : (_overheadContentHeight -
- _availableSpaceAboveContent!) *
- 2,
- ),
- CompositedTransformTarget(
- link: MatrixState.pAnyState
- .layerLinkAndKey(
- 'overlay_message_${widget.event.eventId}',
- )
- .link,
- child: OverlayCenterContent(
- event: widget.event,
- messageHeight: _originalMessageSize.height,
- messageWidth: widget
- .overlayController.showingExtraContent
- ? max(_originalMessageSize.width, 150)
- : _originalMessageSize.width,
- overlayController: widget.overlayController,
- chatController: widget.chatController,
- nextEvent: widget.nextEvent,
- prevEvent: widget.prevEvent,
- hasReactions: _hasReactions,
- // sizeAnimation: _messageSizeAnimation,
- isTransitionAnimation: true,
- readingAssistanceMode: widget
- .overlayController.readingAssistanceMode,
- ),
- ),
- const SizedBox(height: 4.0),
- SelectModeButtons(
- controller: widget.chatController,
- overlayController: widget.overlayController,
- lauchPractice: () {},
- // lauchPractice: () {
- // _setReadingAssistanceMode(
- // ReadingAssistanceMode.practiceMode,
- // );
- // widget.overlayController
- // .updateSelectedSpan(null);
- // },
- ),
- ],
- ),
+ SizedBox(
+ width: mediaQuery!.size.width -
+ columnWidth -
+ (showDetails ? FluffyThemes.columnWidth : 0),
+ height: mediaQuery!.size.height -
+ mediaQuery!.padding.top -
+ mediaQuery!.padding.bottom,
+ child: Stack(
+ alignment:
+ ownMessage ? Alignment.centerRight : Alignment.centerLeft,
+ children: [
+ if (!startedTransition) ...[
+ OverMessageOverlay(controller: this),
+ if (shouldScroll)
+ Positioned(
+ top: 0,
+ left: _wordCardLeftOffset,
+ right: messageRightOffset,
+ child: WordCardSwitcher(controller: this),
),
+ ],
+ if (readingAssistanceMode ==
+ ReadingAssistanceMode.practiceMode) ...[
+ CenteredMessage(
+ targetId:
+ "overlay_center_message_${widget.event.eventId}",
+ controller: this,
),
- AnimatedPositioned(
- top: _wordCardTopOffset,
- left: _wordCardLeftOffset,
- right: _messageRightOffset,
- duration: FluffyThemes.animationDuration,
- child: AnimatedSize(
- alignment: _ownMessage
- ? Alignment.bottomRight
- : Alignment.bottomLeft,
- duration: FluffyThemes.animationDuration,
- child: _wordCardTopOffset == null
- ? const SizedBox()
- : widget.pangeaMessageEvent != null &&
- widget.overlayController.selectedToken !=
- null
- ? ReadingAssistanceContent(
- pangeaMessageEvent:
- widget.pangeaMessageEvent!,
- overlayController:
- widget.overlayController,
- )
- : MessageReactionPicker(
- chatController: widget.chatController,
- ),
+ PracticeModeTransitionAnimation(
+ targetId:
+ "overlay_center_message_${widget.event.eventId}",
+ controller: this,
+ ),
+ Positioned(
+ left: 0,
+ right: 0,
+ bottom: 20,
+ child: ReadingAssistanceInputBar(
+ widget.chatController,
+ widget.overlayController,
),
),
],
- ),
+ ],
),
),
],
),
- if (_showDetails)
+ if (showDetails)
const SizedBox(
width: FluffyThemes.columnWidth,
),
diff --git a/lib/pangea/toolbar/widgets/over_message_overlay.dart b/lib/pangea/toolbar/widgets/over_message_overlay.dart
new file mode 100644
index 000000000..38e5b750c
--- /dev/null
+++ b/lib/pangea/toolbar/widgets/over_message_overlay.dart
@@ -0,0 +1,87 @@
+import 'dart:math';
+
+import 'package:flutter/material.dart';
+
+import 'package:fluffychat/config/themes.dart';
+import 'package:fluffychat/pangea/toolbar/enums/reading_assistance_mode_enum.dart';
+import 'package:fluffychat/pangea/toolbar/widgets/message_selection_positioner.dart';
+import 'package:fluffychat/pangea/toolbar/widgets/overlay_center_content.dart';
+import 'package:fluffychat/pangea/toolbar/widgets/select_mode_buttons.dart';
+import 'package:fluffychat/pangea/toolbar/widgets/word_card_switcher.dart';
+import 'package:fluffychat/widgets/matrix.dart';
+
+class OverMessageOverlay extends StatelessWidget {
+ final MessageSelectionPositionerState controller;
+ const OverMessageOverlay({super.key, required this.controller});
+
+ @override
+ Widget build(BuildContext context) {
+ return Align(
+ alignment: controller.ownMessage ? Alignment.topRight : Alignment.topLeft,
+ child: Padding(
+ padding: EdgeInsets.only(
+ left: controller.messageLeftOffset ?? 0.0,
+ right: controller.messageRightOffset ?? 0.0,
+ ),
+ child: GestureDetector(
+ onTap: controller.widget.chatController.clearSelectedEvents,
+ child: SingleChildScrollView(
+ controller: controller.scrollController,
+ child: Column(
+ crossAxisAlignment: controller.ownMessage
+ ? CrossAxisAlignment.end
+ : CrossAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ AnimatedContainer(
+ duration: FluffyThemes.animationDuration,
+ height: max(0, controller.spaceAboveContent),
+ width: controller.mediaQuery!.size.width -
+ controller.columnWidth -
+ (controller.showDetails ? FluffyThemes.columnWidth : 0),
+ ),
+ if (!controller.shouldScroll)
+ WordCardSwitcher(controller: controller),
+ CompositedTransformTarget(
+ link: MatrixState.pAnyState
+ .layerLinkAndKey(
+ 'overlay_message_${controller.widget.event.eventId}',
+ )
+ .link,
+ child: OverlayCenterContent(
+ event: controller.widget.event,
+ messageHeight: controller.originalMessageSize.height,
+ messageWidth:
+ controller.widget.overlayController.showingExtraContent
+ ? max(controller.originalMessageSize.width, 150)
+ : controller.originalMessageSize.width,
+ overlayController: controller.widget.overlayController,
+ chatController: controller.widget.chatController,
+ nextEvent: controller.widget.nextEvent,
+ prevEvent: controller.widget.prevEvent,
+ hasReactions: controller.hasReactions,
+ isTransitionAnimation: true,
+ readingAssistanceMode: controller.readingAssistanceMode,
+ overlayKey: MatrixState.pAnyState
+ .layerLinkAndKey(
+ 'overlay_message_${controller.widget.event.eventId}',
+ )
+ .key,
+ ),
+ ),
+ const SizedBox(height: 4.0),
+ SelectModeButtons(
+ controller: controller.widget.chatController,
+ overlayController: controller.widget.overlayController,
+ lauchPractice: () => controller.setReadingAssistanceMode(
+ ReadingAssistanceMode.practiceMode,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/pangea/toolbar/widgets/overlay_center_content.dart b/lib/pangea/toolbar/widgets/overlay_center_content.dart
index aa23be54f..3c28384a4 100644
--- a/lib/pangea/toolbar/widgets/overlay_center_content.dart
+++ b/lib/pangea/toolbar/widgets/overlay_center_content.dart
@@ -8,7 +8,6 @@ import 'package:fluffychat/pangea/toolbar/enums/reading_assistance_mode_enum.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/widgets/matrix.dart';
class OverlayCenterContent extends StatelessWidget {
final Event event;
@@ -29,10 +28,12 @@ class OverlayCenterContent extends StatelessWidget {
final bool isTransitionAnimation;
final ReadingAssistanceMode? readingAssistanceMode;
+ final LabeledGlobalKey? overlayKey;
+
const OverlayCenterContent({
required this.event,
- required this.messageHeight,
- required this.messageWidth,
+ this.messageHeight,
+ this.messageWidth,
required this.overlayController,
required this.chatController,
required this.nextEvent,
@@ -42,6 +43,7 @@ class OverlayCenterContent extends StatelessWidget {
this.sizeAnimation,
this.isTransitionAnimation = false,
this.readingAssistanceMode,
+ this.overlayKey,
super.key,
});
@@ -63,11 +65,7 @@ class OverlayCenterContent extends StatelessWidget {
MeasureRenderBox(
onChange: onChangeMessageSize,
child: OverlayMessage(
- key: isTransitionAnimation
- ? MatrixState.pAnyState
- .layerLinkAndKey('overlay_message_${event.eventId}')
- .key
- : null,
+ key: overlayKey,
event,
immersionMode: chatController.choreographer.immersionMode,
controller: chatController,
@@ -78,13 +76,8 @@ class OverlayCenterContent extends StatelessWidget {
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,
+ messageWidth: messageWidth,
+ messageHeight: messageHeight,
isTransitionAnimation: isTransitionAnimation,
readingAssistanceMode: readingAssistanceMode,
),
diff --git a/lib/pangea/toolbar/widgets/overlay_header.dart b/lib/pangea/toolbar/widgets/overlay_header.dart
deleted file mode 100644
index d748aac47..000000000
--- a/lib/pangea/toolbar/widgets/overlay_header.dart
+++ /dev/null
@@ -1,171 +0,0 @@
-import 'package:flutter/material.dart';
-
-import 'package:material_symbols_icons/symbols.dart';
-import 'package:matrix/matrix.dart';
-
-import 'package:fluffychat/config/app_config.dart';
-import 'package:fluffychat/l10n/l10n.dart';
-import 'package:fluffychat/pages/chat/chat.dart';
-import 'package:fluffychat/pangea/events/utils/report_message.dart';
-
-class OverlayHeader extends StatefulWidget {
- final ChatController controller;
-
- const OverlayHeader({
- required this.controller,
- super.key,
- });
-
- @override
- State createState() => OverlayHeaderState();
-}
-
-class OverlayHeaderState extends State {
- ChatController get controller => widget.controller;
-
- final ScrollController _scrollController = ScrollController();
-
- @override
- void dispose() {
- _scrollController.dispose();
- super.dispose();
- }
-
- @override
- Widget build(BuildContext context) {
- final l10n = L10n.of(context);
- final theme = Theme.of(context);
- final pinned = controller.selectedEvents.length == 1 &&
- controller.room.pinnedEventIds.contains(
- controller.selectedEvents.first.eventId,
- );
- return Container(
- padding: const EdgeInsets.symmetric(horizontal: 10),
- decoration: BoxDecoration(
- borderRadius: const BorderRadius.only(
- bottomLeft: Radius.circular(AppConfig.borderRadius),
- bottomRight: Radius.circular(AppConfig.borderRadius),
- ),
- color: theme.appBarTheme.backgroundColor ??
- theme.colorScheme.surfaceContainerHighest,
- ),
- height: theme.appBarTheme.toolbarHeight ?? AppConfig.defaultHeaderHeight,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.end,
- children: [
- Expanded(
- child: Scrollbar(
- thumbVisibility: true,
- controller: _scrollController,
- child: Align(
- alignment: Alignment.centerRight,
- child: SingleChildScrollView(
- scrollDirection: Axis.horizontal,
- controller: _scrollController,
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 8.0),
- child: Row(
- children: [
- // #Pangea
- // if (controller.selectedEvents.length == 1)
- if (controller.selectedEvents.length == 1 &&
- controller.room.canSendDefaultMessages)
- // Pangea#
- IconButton(
- icon: const Icon(Symbols.reply_all),
- tooltip: l10n.reply,
- onPressed: controller.replyAction,
- color: theme.colorScheme.primary,
- ),
- IconButton(
- icon: const Icon(Symbols.forward),
- tooltip: l10n.forward,
- onPressed: controller.forwardEventsAction,
- color: theme.colorScheme.primary,
- ),
- if (controller.selectedEvents.length == 1 &&
- controller.selectedEvents.single.messageType ==
- MessageTypes.Text)
- IconButton(
- icon: const Icon(Icons.copy_outlined),
- tooltip: l10n.copy,
- onPressed: controller.copyEventsAction,
- color: theme.colorScheme.primary,
- ),
- if (controller.canSaveSelectedEvent)
- // Use builder context to correctly position the share dialog on iPad
- Builder(
- builder: (context) => IconButton(
- icon: const Icon(Symbols.download),
- tooltip: L10n.of(context).download,
- onPressed: () =>
- controller.saveSelectedEvent(context),
- color: theme.colorScheme.primary,
- ),
- ),
- if (controller.canPinSelectedEvents)
- IconButton(
- icon: pinned
- ? const Icon(Icons.push_pin)
- : const Icon(Icons.push_pin_outlined),
- onPressed: () {
- controller
- .pinEvent()
- .then((_) => setState(() {}));
- },
- tooltip: pinned ? l10n.unpin : l10n.pinMessage,
- color: theme.colorScheme.primary,
- ),
-
- // if (controller.canEditSelectedEvents &&
- // !controller.selectedEvents.first.isActivityMessage)
- // IconButton(
- // icon: const Icon(Icons.edit_outlined),
- // tooltip: l10n.edit,
- // onPressed: controller.editSelectedEventAction,
- // color: theme.colorScheme.primary,
- // ),
- if (controller.canRedactSelectedEvents)
- IconButton(
- icon: const Icon(Icons.delete_outlined),
- tooltip: l10n.redactMessage,
- onPressed: controller.redactEventsAction,
- color: theme.colorScheme.primary,
- ),
- if (controller.selectedEvents.length == 1)
- IconButton(
- icon: const Icon(Icons.shield_outlined),
- tooltip: l10n.reportMessage,
- onPressed: () {
- final event = controller.selectedEvents.first;
- controller.clearSelectedEvents();
- reportEvent(
- event,
- controller,
- controller.context,
- );
- },
- color: theme.colorScheme.primary,
- ),
- if (controller.selectedEvents.length == 1)
- IconButton(
- icon: const Icon(Icons.info_outlined),
- tooltip: l10n.messageInfo,
- color: theme.colorScheme.primary,
- onPressed: () {
- controller.showEventInfo();
- controller.clearSelectedEvents();
- },
- ),
- ],
- ),
- ),
- ),
- ),
- ),
- ),
- ],
- ),
- );
- }
-}
diff --git a/lib/pangea/toolbar/widgets/overlay_message.dart b/lib/pangea/toolbar/widgets/overlay_message.dart
index e565be942..546ab1258 100644
--- a/lib/pangea/toolbar/widgets/overlay_message.dart
+++ b/lib/pangea/toolbar/widgets/overlay_message.dart
@@ -334,21 +334,23 @@ class OverlayMessage extends StatelessWidget {
);
},
),
- MessageContent(
- displayEvent,
- textColor: textColor,
- linkColor: linkColor,
- borderRadius: borderRadius,
- timeline: timeline,
- pangeaMessageEvent: overlayController.pangeaMessageEvent,
- immersionMode: immersionMode,
- overlayController: overlayController,
- controller: controller,
- nextEvent: nextEvent,
- prevEvent: previousEvent,
- isTransitionAnimation: isTransitionAnimation,
- readingAssistanceMode: readingAssistanceMode,
- selected: true,
+ Flexible(
+ child: MessageContent(
+ displayEvent,
+ textColor: textColor,
+ linkColor: linkColor,
+ borderRadius: borderRadius,
+ timeline: timeline,
+ pangeaMessageEvent: overlayController.pangeaMessageEvent,
+ immersionMode: immersionMode,
+ overlayController: overlayController,
+ controller: controller,
+ nextEvent: nextEvent,
+ prevEvent: previousEvent,
+ isTransitionAnimation: isTransitionAnimation,
+ readingAssistanceMode: readingAssistanceMode,
+ selected: true,
+ ),
),
if (event.hasAggregatedEvents(
timeline,
diff --git a/lib/pangea/toolbar/widgets/practice_activity/emoji_practice_button.dart b/lib/pangea/toolbar/widgets/practice_activity/emoji_practice_button.dart
deleted file mode 100644
index 1ac4aeb10..000000000
--- a/lib/pangea/toolbar/widgets/practice_activity/emoji_practice_button.dart
+++ /dev/null
@@ -1,33 +0,0 @@
-import 'package:flutter/material.dart';
-
-import 'package:fluffychat/pangea/emojis/emoji_stack.dart';
-import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
-import 'package:fluffychat/pangea/toolbar/widgets/practice_activity/word_zoom_activity_button.dart';
-
-class EmojiPracticeButton extends StatelessWidget {
- final PangeaToken token;
- final VoidCallback onPressed;
- final bool isSelected;
-
- const EmojiPracticeButton({
- required this.token,
- required this.onPressed,
- this.isSelected = false,
- super.key,
- });
-
- @override
- Widget build(BuildContext context) {
- final emoji = token.getEmoji();
- return WordZoomActivityButton(
- icon: emoji.isEmpty
- ? const Icon(Icons.add_reaction_outlined)
- : EmojiStack(
- emoji: emoji,
- style: const TextStyle(fontSize: 24),
- ),
- isSelected: isSelected,
- onPressed: onPressed,
- );
- }
-}
diff --git a/lib/pangea/toolbar/widgets/practice_mode_transition_animation.dart b/lib/pangea/toolbar/widgets/practice_mode_transition_animation.dart
new file mode 100644
index 000000000..6f342e574
--- /dev/null
+++ b/lib/pangea/toolbar/widgets/practice_mode_transition_animation.dart
@@ -0,0 +1,200 @@
+import 'package:flutter/material.dart';
+
+import 'package:fluffychat/config/app_config.dart';
+import 'package:fluffychat/config/themes.dart';
+import 'package:fluffychat/pangea/toolbar/widgets/message_selection_positioner.dart';
+import 'package:fluffychat/pangea/toolbar/widgets/overlay_center_content.dart';
+import 'package:fluffychat/widgets/matrix.dart';
+
+class PracticeModeTransitionAnimation extends StatefulWidget {
+ final String targetId;
+ final MessageSelectionPositionerState controller;
+ const PracticeModeTransitionAnimation({
+ super.key,
+ required this.targetId,
+ required this.controller,
+ });
+
+ @override
+ State createState() =>
+ PracticeModeTransitionAnimationState();
+}
+
+class PracticeModeTransitionAnimationState
+ extends State
+ with SingleTickerProviderStateMixin {
+ AnimationController? _animationController;
+ Animation? _offsetAnimation;
+ Animation? _sizeAnimation;
+
+ bool _finishedAnimation = false;
+
+ RenderBox? get _centerMessageRenderBox {
+ try {
+ return MatrixState.pAnyState.getRenderBox(widget.targetId);
+ } catch (e) {
+ return null;
+ }
+ }
+
+ Offset? get _centerMessageOffset {
+ final renderBox = _centerMessageRenderBox;
+ if (renderBox == null) {
+ return null;
+ }
+ return renderBox.localToGlobal(Offset.zero);
+ }
+
+ Size? get _centerMessageSize {
+ final renderBox = _centerMessageRenderBox;
+ if (renderBox == null) {
+ return null;
+ }
+ return renderBox.size;
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ final startOffset = Offset(
+ widget.controller.ownMessage
+ ? widget.controller.messageRightOffset!
+ : widget.controller.messageLeftOffset!,
+ widget.controller.overlayMessageOffset!.dy,
+ );
+
+ final endOffset = Offset(
+ _centerMessageOffset!.dx - widget.controller.columnWidth,
+ _centerMessageOffset!.dy,
+ );
+
+ _animationController = AnimationController(
+ vsync: this,
+ duration: widget.controller.transitionAnimationDuration,
+ // duration: const Duration(seconds: 3),
+ );
+
+ _offsetAnimation = Tween(
+ begin: startOffset,
+ end: endOffset,
+ ).animate(
+ CurvedAnimation(
+ parent: _animationController!,
+ curve: FluffyThemes.animationCurve,
+ ),
+ );
+
+ final startSize = Size(
+ widget.controller.originalMessageSize.width,
+ widget.controller.originalMessageSize.height,
+ );
+
+ _sizeAnimation = Tween(
+ begin: startSize,
+ end: _centerMessageSize!,
+ ).animate(
+ CurvedAnimation(
+ parent: _animationController!,
+ curve: FluffyThemes.animationCurve,
+ ),
+ );
+
+ widget.controller.onStartedTransition();
+ _animationController!.forward().then((_) {
+ widget.controller.onFinishedTransition();
+ if (mounted) {
+ setState(() {
+ _finishedAnimation = true;
+ });
+ }
+ });
+ });
+ }
+
+ @override
+ void dispose() {
+ _animationController?.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ if (_offsetAnimation == null || _finishedAnimation) {
+ return const SizedBox();
+ }
+
+ return AnimatedBuilder(
+ animation: _offsetAnimation!,
+ builder: (context, child) {
+ return Positioned(
+ top: _offsetAnimation!.value.dy,
+ left:
+ widget.controller.ownMessage ? null : _offsetAnimation!.value.dx,
+ right:
+ widget.controller.ownMessage ? _offsetAnimation!.value.dx : null,
+ child: OverlayCenterContent(
+ event: widget.controller.widget.event,
+ overlayController: widget.controller.widget.overlayController,
+ chatController: widget.controller.widget.chatController,
+ nextEvent: widget.controller.widget.nextEvent,
+ prevEvent: widget.controller.widget.prevEvent,
+ hasReactions: widget.controller.hasReactions,
+ sizeAnimation: _sizeAnimation,
+ readingAssistanceMode: widget.controller.readingAssistanceMode,
+ ),
+ );
+ },
+ );
+ }
+}
+
+class CenteredMessage extends StatelessWidget {
+ final String targetId;
+ final MessageSelectionPositionerState controller;
+
+ const CenteredMessage({
+ super.key,
+ required this.targetId,
+ required this.controller,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Opacity(
+ opacity: controller.finishedTransition ? 1.0 : 0.0,
+ child: GestureDetector(
+ onTap: controller.widget.chatController.clearSelectedEvents,
+ child: SingleChildScrollView(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ SizedBox(
+ width:
+ controller.mediaQuery!.size.width - controller.columnWidth,
+ height: 20.0,
+ ),
+ OverlayCenterContent(
+ event: controller.widget.event,
+ overlayController: controller.widget.overlayController,
+ chatController: controller.widget.chatController,
+ nextEvent: controller.widget.nextEvent,
+ prevEvent: controller.widget.prevEvent,
+ hasReactions: controller.hasReactions,
+ overlayKey: MatrixState.pAnyState
+ .layerLinkAndKey(
+ "overlay_center_message_${controller.widget.event.eventId}",
+ )
+ .key,
+ readingAssistanceMode: controller.readingAssistanceMode,
+ ),
+ const SizedBox(
+ height: AppConfig.readingAssistanceInputBarHeight + 60.0,
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/pangea/toolbar/widgets/stt_transcript_tokens.dart b/lib/pangea/toolbar/widgets/stt_transcript_tokens.dart
index 98777076b..fcea78ddf 100644
--- a/lib/pangea/toolbar/widgets/stt_transcript_tokens.dart
+++ b/lib/pangea/toolbar/widgets/stt_transcript_tokens.dart
@@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/message_token_text/token_position_model.dart';
import 'package:fluffychat/pangea/toolbar/models/speech_to_text_models.dart';
-import 'package:fluffychat/widgets/matrix.dart';
class SttTranscriptTokens extends StatelessWidget {
final SpeechToTextModel model;
@@ -55,29 +54,21 @@ class SttTranscriptTokens extends StatelessWidget {
final selected = isSelected?.call(token) ?? false;
return WidgetSpan(
- child: CompositedTransformTarget(
- link: MatrixState.pAnyState
- .layerLinkAndKey(token.text.uniqueKey)
- .link,
- child: MouseRegion(
- key: MatrixState.pAnyState
- .layerLinkAndKey(token.text.uniqueKey)
- .key,
- cursor: SystemMouseCursors.click,
- child: GestureDetector(
- behavior: HitTestBehavior.translucent,
- onTap: onClick != null ? () => onClick?.call(token) : null,
- child: RichText(
- text: TextSpan(
- text: text,
- style: (style ?? DefaultTextStyle.of(context).style)
- .copyWith(
- decoration: TextDecoration.underline,
- decorationThickness: 4,
- decorationColor: selected
- ? Theme.of(context).colorScheme.primary
- : Colors.white.withAlpha(0),
- ),
+ child: MouseRegion(
+ cursor: SystemMouseCursors.click,
+ child: GestureDetector(
+ behavior: HitTestBehavior.translucent,
+ onTap: onClick != null ? () => onClick?.call(token) : null,
+ child: RichText(
+ text: TextSpan(
+ text: text,
+ style:
+ (style ?? DefaultTextStyle.of(context).style).copyWith(
+ decoration: TextDecoration.underline,
+ decorationThickness: 4,
+ decorationColor: selected
+ ? Theme.of(context).colorScheme.primary
+ : Colors.white.withAlpha(0),
),
),
),
diff --git a/lib/pangea/toolbar/widgets/toolbar_button_and_progress_column.dart b/lib/pangea/toolbar/widgets/toolbar_button_and_progress_column.dart
deleted file mode 100644
index 193b5ec9c..000000000
--- a/lib/pangea/toolbar/widgets/toolbar_button_and_progress_column.dart
+++ /dev/null
@@ -1,104 +0,0 @@
-import 'dart:math';
-
-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/pangea/toolbar/enums/message_mode_enum.dart';
-import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
-
-class ToolbarButtonAndProgressColumn extends StatelessWidget {
- final Event event;
- final MessageOverlayController overlayController;
- final double height;
- final double width;
-
- const ToolbarButtonAndProgressColumn({
- required this.event,
- required this.overlayController,
- required this.height,
- required this.width,
- super.key,
- });
-
- double? get proportionOfActivitiesCompleted =>
- overlayController.pangeaMessageEvent?.proportionOfActivitiesCompleted;
-
- static const double iconWidth = 36.0;
- static const double buttonSize = 40.0;
- static const barMargin =
- EdgeInsets.symmetric(horizontal: iconWidth / 2, vertical: buttonSize / 2);
-
- @override
- Widget build(BuildContext context) {
- if (event.messageType == MessageTypes.Audio ||
- !(overlayController.pangeaMessageEvent?.messageDisplayLangIsL2 ??
- false)) {
- return SizedBox(height: height, width: width);
- }
-
- return SizedBox(
- height: height,
- width: width,
- child: Stack(
- alignment: Alignment.bottomCenter,
- children: [
- Stack(
- alignment: Alignment.bottomCenter,
- children: [
- Container(
- width: width,
- height: height,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(AppConfig.borderRadius),
- color: MessageModeExtension.barAndLockedButtonColor(context),
- ),
- margin: barMargin,
- ),
- AnimatedContainer(
- duration: FluffyThemes.animationDuration,
- width: width,
- height: overlayController.isPracticeComplete
- ? height
- : min(
- height,
- height * proportionOfActivitiesCompleted!,
- ),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(AppConfig.borderRadius),
- color: AppConfig.gold,
- ),
- margin: barMargin,
- ),
- Positioned(
- bottom: height * MessageMode.noneSelected.pointOnBar -
- buttonSize / 2 -
- barMargin.vertical / 2,
- child: Container(
- decoration: BoxDecoration(
- color: overlayController.isPracticeComplete
- ? AppConfig.gold
- : MessageModeExtension.barAndLockedButtonColor(context),
- shape: BoxShape.circle,
- ),
- height: buttonSize,
- width: buttonSize,
- alignment: Alignment.center,
- child: Icon(
- Icons.star_rounded,
- color: overlayController.isPracticeComplete
- ? Colors.white
- : Theme.of(context).colorScheme.onSurface,
- size: 30,
- ),
- ),
- ),
- ],
- ),
- ],
- ),
- );
- }
-}
diff --git a/lib/pangea/toolbar/widgets/word_card_switcher.dart b/lib/pangea/toolbar/widgets/word_card_switcher.dart
new file mode 100644
index 000000000..cd2285927
--- /dev/null
+++ b/lib/pangea/toolbar/widgets/word_card_switcher.dart
@@ -0,0 +1,28 @@
+import 'package:flutter/material.dart';
+
+import 'package:fluffychat/config/themes.dart';
+import 'package:fluffychat/pangea/toolbar/widgets/message_selection_positioner.dart';
+import 'package:fluffychat/pangea/toolbar/widgets/reading_assistance_content.dart';
+
+class WordCardSwitcher extends StatelessWidget {
+ final MessageSelectionPositionerState controller;
+ const WordCardSwitcher({super.key, required this.controller});
+
+ @override
+ Widget build(BuildContext context) {
+ return AnimatedSize(
+ alignment:
+ controller.ownMessage ? Alignment.bottomRight : Alignment.bottomLeft,
+ duration: FluffyThemes.animationDuration,
+ child: controller.widget.pangeaMessageEvent != null &&
+ controller.widget.overlayController.selectedToken != null
+ ? ReadingAssistanceContent(
+ pangeaMessageEvent: controller.widget.pangeaMessageEvent!,
+ overlayController: controller.widget.overlayController,
+ )
+ : MessageReactionPicker(
+ chatController: controller.widget.chatController,
+ ),
+ );
+ }
+}