Message overlay updates (#3522)
* added slide animation to overlay * re-enable practice mode * chore: position overlay over original message * chore: fix spacing on mobile * chore: remove unreferenced files
This commit is contained in:
parent
b06d368058
commit
641a18a1fa
15 changed files with 600 additions and 1101 deletions
|
|
@ -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#
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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: <Widget>[
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<MessageSelectionPositioner>
|
||||
with TickerProviderStateMixin {
|
||||
// late AnimationController _animationController;
|
||||
|
||||
// Offset? _centeredMessageOffset;
|
||||
// Size? _centeredMessageSize;
|
||||
|
||||
// Size? _tooltipSize;
|
||||
|
||||
// final Completer _centeredMessageCompleter = Completer();
|
||||
// final Completer _tooltipCompleter = Completer();
|
||||
|
||||
// MessageMode _currentMode = MessageMode.noneSelected;
|
||||
|
||||
// Animation<Offset>? _overlayOffsetAnimation;
|
||||
// Animation<Size>? _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<MessageSelectionPositioner>
|
|||
_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<void> _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<Size>(
|
||||
// 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<Offset>(
|
||||
// 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<T>(
|
||||
Function runner,
|
||||
String errorMessage,
|
||||
|
|
@ -498,10 +134,16 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
|
|||
}
|
||||
}
|
||||
|
||||
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<MessageSelectionPositioner>
|
|||
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?>(
|
||||
MediaQueryData? get mediaQuery => _runWithLogging<MediaQueryData?>(
|
||||
() => 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<MessageSelectionPositioner>
|
|||
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<MessageSelectionPositioner>
|
|||
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<RenderBox?>(
|
||||
|
|
@ -565,6 +204,18 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
|
|||
|
||||
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<RenderBox?>(
|
||||
() => MatrixState.pAnyState.getRenderBox(
|
||||
widget.event.eventId,
|
||||
|
|
@ -585,7 +236,7 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
|
|||
}
|
||||
|
||||
/// 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<MessageSelectionPositioner>
|
|||
);
|
||||
}
|
||||
|
||||
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<MessageSelectionPositioner>
|
|||
: 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<MessageSelectionPositioner>
|
|||
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,
|
||||
),
|
||||
|
|
|
|||
87
lib/pangea/toolbar/widgets/over_message_overlay.dart
Normal file
87
lib/pangea/toolbar/widgets/over_message_overlay.dart
Normal file
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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<OverlayHeader> createState() => OverlayHeaderState();
|
||||
}
|
||||
|
||||
class OverlayHeaderState extends State<OverlayHeader> {
|
||||
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();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<PracticeModeTransitionAnimation> createState() =>
|
||||
PracticeModeTransitionAnimationState();
|
||||
}
|
||||
|
||||
class PracticeModeTransitionAnimationState
|
||||
extends State<PracticeModeTransitionAnimation>
|
||||
with SingleTickerProviderStateMixin {
|
||||
AnimationController? _animationController;
|
||||
Animation<Offset>? _offsetAnimation;
|
||||
Animation<Size>? _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<Offset>(
|
||||
begin: startOffset,
|
||||
end: endOffset,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _animationController!,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
),
|
||||
);
|
||||
|
||||
final startSize = Size(
|
||||
widget.controller.originalMessageSize.width,
|
||||
widget.controller.originalMessageSize.height,
|
||||
);
|
||||
|
||||
_sizeAnimation = Tween<Size>(
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
28
lib/pangea/toolbar/widgets/word_card_switcher.dart
Normal file
28
lib/pangea/toolbar/widgets/word_card_switcher.dart
Normal file
|
|
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue