refactor: new message selection mode
This commit is contained in:
parent
74e1032c1c
commit
aeb92b1b89
10 changed files with 988 additions and 384 deletions
|
|
@ -27,9 +27,10 @@ abstract class AppConfig {
|
|||
static const bool allowOtherHomeservers = true;
|
||||
static const bool enableRegistration = true;
|
||||
// #Pangea
|
||||
static const double toolbarMaxHeight = 250.0;
|
||||
static const double toolbarMaxHeight = 225.0;
|
||||
static const double toolbarMinHeight = 150.0;
|
||||
static const double toolbarMinWidth = 350.0;
|
||||
static const double toolbarMenuHeight = 300.0;
|
||||
static const double defaultHeaderHeight = 56.0;
|
||||
static const double toolbarButtonsHeight = 50.0;
|
||||
static const double toolbarSpacing = 8.0;
|
||||
|
|
@ -89,6 +90,8 @@ abstract class AppConfig {
|
|||
static String _privacyUrl = "https://www.pangeachat.com/privacy";
|
||||
//Pangea#
|
||||
|
||||
static const Set<String> defaultReactions = {'👍', '❤️', '😂', '😮', '😢'};
|
||||
|
||||
static String get privacyUrl => _privacyUrl;
|
||||
// #Pangea
|
||||
// static const String website = 'https://fluffychat.im';
|
||||
|
|
|
|||
|
|
@ -5031,5 +5031,6 @@
|
|||
}
|
||||
},
|
||||
"failedToFetchTranscription": "Failed to fetch transcription",
|
||||
"deleteEmptySpaceDesc": "The space will be deleted for all participants. This action cannot be undone."
|
||||
"deleteEmptySpaceDesc": "The space will be deleted for all participants. This action cannot be undone.",
|
||||
"customReaction": "Custom reaction"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2099,10 +2099,10 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
OverlayUtil.showOverlay(
|
||||
context: context,
|
||||
child: overlayEntry!,
|
||||
transformTargetId: "",
|
||||
position: OverlayPositionEnum.centered,
|
||||
onDismiss: clearSelectedEvents,
|
||||
blurBackground: true,
|
||||
backgroundColor: Colors.black,
|
||||
);
|
||||
|
||||
// select the message
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class OverlayUtil {
|
|||
static showOverlay({
|
||||
required BuildContext context,
|
||||
required Widget child,
|
||||
required String transformTargetId,
|
||||
String? transformTargetId,
|
||||
backDropToDismiss = true,
|
||||
blurBackground = false,
|
||||
Color? borderColor,
|
||||
|
|
@ -37,6 +37,13 @@ class OverlayUtil {
|
|||
bool canPop = true,
|
||||
}) {
|
||||
try {
|
||||
if (position == OverlayPositionEnum.transform) {
|
||||
assert(
|
||||
transformTargetId != null,
|
||||
"transformTargetId must be provided when position is OverlayPositionEnum.transform",
|
||||
);
|
||||
}
|
||||
|
||||
if (closePrevOverlay) {
|
||||
MatrixState.pAnyState.closeOverlay();
|
||||
}
|
||||
|
|
@ -77,7 +84,7 @@ class OverlayUtil {
|
|||
followerAnchor:
|
||||
followerAnchor ?? Alignment.bottomCenter,
|
||||
link: MatrixState.pAnyState
|
||||
.layerLinkAndKey(transformTargetId)
|
||||
.layerLinkAndKey(transformTargetId!)
|
||||
.link,
|
||||
showWhenUnlinked: false,
|
||||
offset: offset ?? Offset.zero,
|
||||
|
|
|
|||
|
|
@ -217,16 +217,16 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
|
||||
updateSelectedSpan(widget._initialSelectedToken!.text);
|
||||
|
||||
int retries = 0;
|
||||
while (retries < 5 &&
|
||||
selectedToken != null &&
|
||||
!MatrixState.pAnyState.isOverlayOpen(
|
||||
selectedToken!.text.uniqueKey,
|
||||
)) {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
_showReadingAssistanceContent();
|
||||
retries++;
|
||||
}
|
||||
// int retries = 0;
|
||||
// while (retries < 5 &&
|
||||
// selectedToken != null &&
|
||||
// !MatrixState.pAnyState.isOverlayOpen(
|
||||
// selectedToken!.text.uniqueKey,
|
||||
// )) {
|
||||
// await Future.delayed(const Duration(milliseconds: 100));
|
||||
// _showReadingAssistanceContent();
|
||||
// retries++;
|
||||
// }
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
|
@ -296,9 +296,9 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
}
|
||||
|
||||
if (mounted) setState(() {});
|
||||
Future.delayed(const Duration(milliseconds: 10), () {
|
||||
_showReadingAssistanceContent();
|
||||
});
|
||||
// Future.delayed(const Duration(milliseconds: 10), () {
|
||||
// _showReadingAssistanceContent();
|
||||
// });
|
||||
}
|
||||
|
||||
void _showReadingAssistanceContent() {
|
||||
|
|
|
|||
|
|
@ -8,19 +8,16 @@ import 'package:matrix/matrix.dart';
|
|||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/setting_keys.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
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/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/enums/reading_assistance_mode_enum.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/reading_assistance_input_row/overlay_footer.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/measure_render_box.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/overlay_center_content.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/overlay_header.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/reading_assistance_content.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/select_mode_buttons.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
|
@ -73,6 +70,8 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
|
|||
StreamSubscription? _reactionSubscription;
|
||||
StreamSubscription? _contentChangedSubscription;
|
||||
|
||||
ScrollController? _scrollController;
|
||||
|
||||
final _animationDuration = const Duration(
|
||||
milliseconds: AppConfig.overlayAnimationDuration,
|
||||
// seconds: 5,
|
||||
|
|
@ -81,6 +80,22 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
|
|||
@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,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
_currentMode = widget.overlayController.toolbarMode;
|
||||
_animationController = AnimationController(
|
||||
vsync: this,
|
||||
|
|
@ -144,6 +159,7 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
|
|||
_animationController.dispose();
|
||||
_reactionSubscription?.cancel();
|
||||
_contentChangedSubscription?.cancel();
|
||||
_scrollController?.dispose();
|
||||
MatrixState.pangeaController.matrixState.audioPlayer
|
||||
?..stop()
|
||||
..dispose();
|
||||
|
|
@ -349,6 +365,62 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
|
|||
null,
|
||||
);
|
||||
|
||||
// Offset? get _overlayMessageOffset =>
|
||||
// _overlayMessageRenderBox?.localToGlobal(Offset.zero);
|
||||
|
||||
Size? get _overlayMessageSize => _overlayMessageRenderBox?.size;
|
||||
|
||||
// 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;
|
||||
// }
|
||||
|
||||
double get _neededTopSpace =>
|
||||
(widget.pangeaMessageEvent != null &&
|
||||
widget.overlayController.selectedToken != null
|
||||
? AppConfig.toolbarMaxHeight
|
||||
: 40.0) +
|
||||
4.0;
|
||||
|
||||
double? get _occupiedSpace {
|
||||
if (_overlayMessageSize == null) return null;
|
||||
return _overlayMessageSize!.height +
|
||||
_reactionsHeight +
|
||||
AppConfig.toolbarMenuHeight;
|
||||
}
|
||||
|
||||
double? get _wordCardTopOffset {
|
||||
if (_overlayMessageSize == null ||
|
||||
_mediaQuery == null ||
|
||||
_occupiedSpace == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final availableSpace = (_mediaQuery!.size.height - _occupiedSpace!) / 2;
|
||||
|
||||
if (availableSpace >= _neededTopSpace) {
|
||||
return availableSpace - _neededTopSpace;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Size get _defaultMessageSize => const Size(FluffyThemes.columnWidth / 2, 100);
|
||||
|
||||
/// The size of the message in the chat list (as opposed to the expanded size in the center overlay)
|
||||
|
|
@ -491,10 +563,13 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
|
|||
_reactionsHeight;
|
||||
}
|
||||
|
||||
double get _messageLeftOffset => max(
|
||||
_originalMessageOffset.dx - _columnWidth - _horizontalPadding,
|
||||
0,
|
||||
);
|
||||
double get _messageLeftOffset {
|
||||
if (_ownMessage) return 0;
|
||||
return max(
|
||||
_originalMessageOffset.dx - _columnWidth - _horizontalPadding,
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
double get _messageRightOffset {
|
||||
if (_mediaQuery == null || !_ownMessage) {
|
||||
|
|
@ -587,225 +662,577 @@ class MessageSelectionPositionerState extends State<MessageSelectionPositioner>
|
|||
|
||||
widget.overlayController.maxWidth = _toolbarMaxWidth;
|
||||
|
||||
return Stack(
|
||||
debugPrint(
|
||||
"width: ${_mediaQuery!.size.width - _columnWidth - (_showDetails ? FluffyThemes.columnWidth : 0)}",
|
||||
);
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: IgnorePointer(
|
||||
child: AnimatedOpacity(
|
||||
duration: _animationDuration,
|
||||
opacity: _readingAssistanceModeOpacity,
|
||||
child: Container(
|
||||
height: double.infinity,
|
||||
width: double.infinity,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: _horizontalPadding,
|
||||
right: _horizontalPadding,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
width: _mediaQuery!.size.width -
|
||||
_columnWidth -
|
||||
(_showDetails ? FluffyThemes.columnWidth : 0),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
alignment: _ownMessage
|
||||
? Alignment.centerRight
|
||||
: Alignment.centerLeft,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: _mediaQuery?.padding.top ?? 0),
|
||||
OverlayHeader(controller: widget.chatController),
|
||||
],
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: widget.chatController.clearSelectedEvents,
|
||||
child: SingleChildScrollView(
|
||||
controller: _scrollController,
|
||||
padding: EdgeInsets.only(
|
||||
left: _messageLeftOffset + _horizontalPadding,
|
||||
right: _messageRightOffset + _horizontalPadding,
|
||||
),
|
||||
const Expanded(
|
||||
flex: 3,
|
||||
child: SizedBox.shrink(),
|
||||
),
|
||||
Opacity(
|
||||
opacity: _readingAssistanceMode ==
|
||||
ReadingAssistanceMode.practiceMode
|
||||
? 1.0
|
||||
: 0.0,
|
||||
child: OverlayCenterContent(
|
||||
event: widget.event,
|
||||
messageHeight: null,
|
||||
messageWidth: null,
|
||||
maxWidth: widget.overlayController.maxWidth,
|
||||
overlayController: widget.overlayController,
|
||||
chatController: widget.chatController,
|
||||
pangeaMessageEvent: widget.pangeaMessageEvent,
|
||||
nextEvent: widget.nextEvent,
|
||||
prevEvent: widget.prevEvent,
|
||||
hasReactions: _hasReactions,
|
||||
onChangeMessageSize: _setCenteredMessageSize,
|
||||
isTransitionAnimation: false,
|
||||
maxHeight: _mediaQuery!.size.height -
|
||||
_headerHeight -
|
||||
_footerHeight -
|
||||
AppConfig.toolbarSpacing * 2 -
|
||||
_selectionButtonsHeight,
|
||||
readingAssistanceMode: _readingAssistanceMode,
|
||||
),
|
||||
),
|
||||
const Expanded(
|
||||
flex: 1,
|
||||
child: SizedBox.shrink(),
|
||||
),
|
||||
Row(
|
||||
child: Column(
|
||||
spacing: 4.0,
|
||||
crossAxisAlignment: _ownMessage
|
||||
? CrossAxisAlignment.end
|
||||
: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
OverlayFooter(
|
||||
controller: widget.chatController,
|
||||
overlayController: widget.overlayController,
|
||||
showToolbarButtons: showPracticeButtons,
|
||||
readingAssistanceMode:
|
||||
_readingAssistanceMode,
|
||||
),
|
||||
SizedBox(
|
||||
height: _mediaQuery?.padding.bottom ?? 0,
|
||||
),
|
||||
],
|
||||
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: _readingAssistanceMode,
|
||||
),
|
||||
),
|
||||
SelectModeButtons(
|
||||
controller: widget.chatController,
|
||||
overlayController: widget.overlayController,
|
||||
lauchPractice: () {
|
||||
_setReadingAssistanceMode(
|
||||
ReadingAssistanceMode.practiceMode,
|
||||
);
|
||||
widget.overlayController
|
||||
.updateSelectedSpan(null);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (_readingAssistanceMode !=
|
||||
ReadingAssistanceMode.practiceMode &&
|
||||
_readingAssistanceMode != null)
|
||||
AnimatedBuilder(
|
||||
animation:
|
||||
_overlayOffsetAnimation ?? _animationController,
|
||||
builder: (context, child) {
|
||||
return Positioned(
|
||||
left: _ownMessage
|
||||
? null
|
||||
: (_overlayOffsetAnimation?.value)?.dx ??
|
||||
_messageLeftOffset,
|
||||
right: _ownMessage
|
||||
? (_overlayOffsetAnimation?.value)?.dx ??
|
||||
_messageRightOffset
|
||||
: null,
|
||||
bottom: (_overlayOffsetAnimation?.value)?.dy ??
|
||||
_originalMessageBottomOffset -
|
||||
_reactionsHeight -
|
||||
_selectionButtonsHeight,
|
||||
child: Column(
|
||||
crossAxisAlignment: _ownMessage
|
||||
? CrossAxisAlignment.end
|
||||
: CrossAxisAlignment.start,
|
||||
children: [
|
||||
OverlayCenterContent(
|
||||
event: widget.event,
|
||||
messageHeight: _originalMessageSize.height,
|
||||
messageWidth: widget
|
||||
.overlayController.showingExtraContent
|
||||
? max(_originalMessageSize.width, 150)
|
||||
: _originalMessageSize.width,
|
||||
maxWidth: widget.overlayController.maxWidth,
|
||||
if (_wordCardTopOffset != null)
|
||||
AnimatedPositioned(
|
||||
top: _wordCardTopOffset,
|
||||
left: _ownMessage
|
||||
? null
|
||||
: _messageLeftOffset + _horizontalPadding,
|
||||
right: _ownMessage
|
||||
? _messageRightOffset + _horizontalPadding
|
||||
: null,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
child: AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
child: widget.pangeaMessageEvent != null &&
|
||||
widget.overlayController.selectedToken != null
|
||||
? ReadingAssistanceContent(
|
||||
pangeaMessageEvent:
|
||||
widget.pangeaMessageEvent!,
|
||||
overlayController: widget.overlayController,
|
||||
)
|
||||
: MessageReactionPicker(
|
||||
chatController: widget.chatController,
|
||||
pangeaMessageEvent: widget.pangeaMessageEvent,
|
||||
nextEvent: widget.nextEvent,
|
||||
prevEvent: widget.prevEvent,
|
||||
hasReactions: _hasReactions,
|
||||
sizeAnimation: _messageSizeAnimation,
|
||||
isTransitionAnimation: true,
|
||||
maxHeight: _mediaQuery!.size.height -
|
||||
_headerHeight -
|
||||
_footerHeight -
|
||||
AppConfig.toolbarSpacing * 2 -
|
||||
_selectionButtonsHeight,
|
||||
readingAssistanceMode: _readingAssistanceMode,
|
||||
),
|
||||
if (showSelectionButtons)
|
||||
SelectModeButtons(
|
||||
overlayController: widget.overlayController,
|
||||
lauchPractice: () {
|
||||
_setReadingAssistanceMode(
|
||||
ReadingAssistanceMode.practiceMode,
|
||||
);
|
||||
widget.overlayController
|
||||
.updateSelectedSpan(null);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (showPracticeButtons)
|
||||
Positioned(
|
||||
top: 0,
|
||||
child: IgnorePointer(
|
||||
child: MeasureRenderBox(
|
||||
onChange: _setTooltipSize,
|
||||
child: Opacity(
|
||||
opacity: 0.0,
|
||||
child: Container(
|
||||
constraints: BoxConstraints(
|
||||
minWidth: 200.0,
|
||||
maxWidth: _toolbarMaxWidth,
|
||||
),
|
||||
child: InstructionsInlineTooltip(
|
||||
instructionsEnum: widget.overlayController
|
||||
.toolbarMode.instructionsEnum ??
|
||||
InstructionsEnum
|
||||
.readingAssistanceOverview,
|
||||
bold: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_centeredMessageTopOffset != null &&
|
||||
_tooltipSize != null &&
|
||||
widget.overlayController.toolbarMode !=
|
||||
MessageMode.noneSelected &&
|
||||
widget.overlayController.selectedToken == null)
|
||||
Positioned(
|
||||
top: max(
|
||||
((_headerHeight + _centeredMessageTopOffset!) / 2) -
|
||||
(_tooltipSize!.height / 2),
|
||||
_headerHeight,
|
||||
),
|
||||
child: Container(
|
||||
constraints: BoxConstraints(
|
||||
minWidth: 200.0,
|
||||
maxWidth: widget.overlayController.maxWidth,
|
||||
),
|
||||
child: InstructionsInlineTooltip(
|
||||
instructionsEnum: widget.overlayController
|
||||
.toolbarMode.instructionsEnum ??
|
||||
InstructionsEnum.readingAssistanceOverview,
|
||||
bold: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (_showDetails)
|
||||
const SizedBox(
|
||||
width: FluffyThemes.columnWidth,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (_showDetails)
|
||||
const SizedBox(
|
||||
width: FluffyThemes.columnWidth,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// return Align(
|
||||
// alignment: Alignment.centerLeft,
|
||||
// child: SizedBox(
|
||||
// width: _mediaQuery!.size.width -
|
||||
// _columnWidth -
|
||||
// (_showDetails ? FluffyThemes.columnWidth : 0),
|
||||
// height: _mediaQuery!.size.height,
|
||||
// child: SingleChildScrollView(
|
||||
// child: Container(
|
||||
// decoration: BoxDecoration(
|
||||
// border: Border.all(color: Colors.green),
|
||||
// ),
|
||||
// padding: EdgeInsets.only(
|
||||
// left: _messageLeftOffset + _horizontalPadding,
|
||||
// right: _messageRightOffset + _horizontalPadding,
|
||||
// ),
|
||||
// child: Stack(
|
||||
// alignment:
|
||||
// _ownMessage ? Alignment.centerRight : Alignment.centerLeft,
|
||||
// children: [
|
||||
// Positioned.fill(
|
||||
// child: InkWell(
|
||||
// onTap: widget.chatController.clearSelectedEvents,
|
||||
// ),
|
||||
// ),
|
||||
// Column(
|
||||
// spacing: 4.0,
|
||||
// crossAxisAlignment: _ownMessage
|
||||
// ? CrossAxisAlignment.end
|
||||
// : CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// if (widget.pangeaMessageEvent != null)
|
||||
// AnimatedSize(
|
||||
// duration: FluffyThemes.animationDuration,
|
||||
// child: widget.overlayController.selectedToken != null
|
||||
// ? ReadingAssistanceContent(
|
||||
// pangeaMessageEvent: widget.pangeaMessageEvent!,
|
||||
// overlayController: widget.overlayController,
|
||||
// )
|
||||
// : const SizedBox.shrink(),
|
||||
// ),
|
||||
// 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: _readingAssistanceMode,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// if (showSelectionButtons)
|
||||
// Positioned(
|
||||
// child: SelectModeButtons(
|
||||
// overlayController: widget.overlayController,
|
||||
// lauchPractice: () {
|
||||
// _setReadingAssistanceMode(
|
||||
// ReadingAssistanceMode.practiceMode,
|
||||
// );
|
||||
// widget.overlayController.updateSelectedSpan(null);
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
|
||||
// return Stack(
|
||||
// children: [
|
||||
// Positioned.fill(
|
||||
// child: IgnorePointer(
|
||||
// child: AnimatedOpacity(
|
||||
// duration: _animationDuration,
|
||||
// opacity: _readingAssistanceModeOpacity,
|
||||
// child: Container(
|
||||
// height: double.infinity,
|
||||
// width: double.infinity,
|
||||
// color: Colors.black,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Padding(
|
||||
// padding: EdgeInsets.only(
|
||||
// left: _horizontalPadding,
|
||||
// right: _horizontalPadding,
|
||||
// ),
|
||||
// child: Row(
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: Stack(
|
||||
// alignment: Alignment.center,
|
||||
// children: [
|
||||
// Column(
|
||||
// children: [
|
||||
// Material(
|
||||
// type: MaterialType.transparency,
|
||||
// child: Column(
|
||||
// children: [
|
||||
// SizedBox(height: _mediaQuery?.padding.top ?? 0),
|
||||
// OverlayHeader(controller: widget.chatController),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// const Expanded(
|
||||
// flex: 3,
|
||||
// child: SizedBox.shrink(),
|
||||
// ),
|
||||
// Opacity(
|
||||
// opacity: _readingAssistanceMode ==
|
||||
// ReadingAssistanceMode.practiceMode
|
||||
// ? 1.0
|
||||
// : 0.0,
|
||||
// child: OverlayCenterContent(
|
||||
// event: widget.event,
|
||||
// messageHeight: null,
|
||||
// messageWidth: null,
|
||||
// maxWidth: widget.overlayController.maxWidth,
|
||||
// overlayController: widget.overlayController,
|
||||
// chatController: widget.chatController,
|
||||
// pangeaMessageEvent: widget.pangeaMessageEvent,
|
||||
// nextEvent: widget.nextEvent,
|
||||
// prevEvent: widget.prevEvent,
|
||||
// hasReactions: _hasReactions,
|
||||
// onChangeMessageSize: _setCenteredMessageSize,
|
||||
// isTransitionAnimation: false,
|
||||
// maxHeight: _mediaQuery!.size.height -
|
||||
// _headerHeight -
|
||||
// _footerHeight -
|
||||
// AppConfig.toolbarSpacing * 2 -
|
||||
// _selectionButtonsHeight,
|
||||
// readingAssistanceMode: _readingAssistanceMode,
|
||||
// ),
|
||||
// ),
|
||||
// const Expanded(
|
||||
// flex: 1,
|
||||
// child: SizedBox.shrink(),
|
||||
// ),
|
||||
// Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// OverlayFooter(
|
||||
// controller: widget.chatController,
|
||||
// overlayController: widget.overlayController,
|
||||
// showToolbarButtons: showPracticeButtons,
|
||||
// readingAssistanceMode:
|
||||
// _readingAssistanceMode,
|
||||
// ),
|
||||
// SizedBox(
|
||||
// height: _mediaQuery?.padding.bottom ?? 0,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// if (_readingAssistanceMode !=
|
||||
// ReadingAssistanceMode.practiceMode &&
|
||||
// _readingAssistanceMode != null)
|
||||
// AnimatedBuilder(
|
||||
// animation:
|
||||
// _overlayOffsetAnimation ?? _animationController,
|
||||
// builder: (context, child) {
|
||||
// return Positioned(
|
||||
// left: _ownMessage
|
||||
// ? null
|
||||
// : (_overlayOffsetAnimation?.value)?.dx ??
|
||||
// _messageLeftOffset,
|
||||
// right: _ownMessage
|
||||
// ? (_overlayOffsetAnimation?.value)?.dx ??
|
||||
// _messageRightOffset
|
||||
// : null,
|
||||
// bottom: (_overlayOffsetAnimation?.value)?.dy ??
|
||||
// _originalMessageBottomOffset -
|
||||
// _reactionsHeight -
|
||||
// _selectionButtonsHeight,
|
||||
// child: Column(
|
||||
// crossAxisAlignment: _ownMessage
|
||||
// ? CrossAxisAlignment.end
|
||||
// : CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// OverlayCenterContent(
|
||||
// event: widget.event,
|
||||
// messageHeight: _originalMessageSize.height,
|
||||
// messageWidth: widget
|
||||
// .overlayController.showingExtraContent
|
||||
// ? max(_originalMessageSize.width, 150)
|
||||
// : _originalMessageSize.width,
|
||||
// maxWidth: widget.overlayController.maxWidth,
|
||||
// overlayController: widget.overlayController,
|
||||
// chatController: widget.chatController,
|
||||
// pangeaMessageEvent: widget.pangeaMessageEvent,
|
||||
// nextEvent: widget.nextEvent,
|
||||
// prevEvent: widget.prevEvent,
|
||||
// hasReactions: _hasReactions,
|
||||
// sizeAnimation: _messageSizeAnimation,
|
||||
// isTransitionAnimation: true,
|
||||
// maxHeight: _mediaQuery!.size.height -
|
||||
// _headerHeight -
|
||||
// _footerHeight -
|
||||
// AppConfig.toolbarSpacing * 2 -
|
||||
// _selectionButtonsHeight,
|
||||
// readingAssistanceMode: _readingAssistanceMode,
|
||||
// ),
|
||||
// if (showSelectionButtons)
|
||||
// SelectModeButtons(
|
||||
// overlayController: widget.overlayController,
|
||||
// lauchPractice: () {
|
||||
// _setReadingAssistanceMode(
|
||||
// ReadingAssistanceMode.practiceMode,
|
||||
// );
|
||||
// widget.overlayController
|
||||
// .updateSelectedSpan(null);
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// if (showPracticeButtons)
|
||||
// Positioned(
|
||||
// top: 0,
|
||||
// child: IgnorePointer(
|
||||
// child: MeasureRenderBox(
|
||||
// onChange: _setTooltipSize,
|
||||
// child: Opacity(
|
||||
// opacity: 0.0,
|
||||
// child: Container(
|
||||
// constraints: BoxConstraints(
|
||||
// minWidth: 200.0,
|
||||
// maxWidth: _toolbarMaxWidth,
|
||||
// ),
|
||||
// child: InstructionsInlineTooltip(
|
||||
// instructionsEnum: widget.overlayController
|
||||
// .toolbarMode.instructionsEnum ??
|
||||
// InstructionsEnum
|
||||
// .readingAssistanceOverview,
|
||||
// bold: true,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// if (_centeredMessageTopOffset != null &&
|
||||
// _tooltipSize != null &&
|
||||
// widget.overlayController.toolbarMode !=
|
||||
// MessageMode.noneSelected &&
|
||||
// widget.overlayController.selectedToken == null)
|
||||
// Positioned(
|
||||
// top: max(
|
||||
// ((_headerHeight + _centeredMessageTopOffset!) / 2) -
|
||||
// (_tooltipSize!.height / 2),
|
||||
// _headerHeight,
|
||||
// ),
|
||||
// child: Container(
|
||||
// constraints: BoxConstraints(
|
||||
// minWidth: 200.0,
|
||||
// maxWidth: widget.overlayController.maxWidth,
|
||||
// ),
|
||||
// child: InstructionsInlineTooltip(
|
||||
// instructionsEnum: widget.overlayController
|
||||
// .toolbarMode.instructionsEnum ??
|
||||
// InstructionsEnum.readingAssistanceOverview,
|
||||
// bold: true,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// if (_showDetails)
|
||||
// const SizedBox(
|
||||
// width: FluffyThemes.columnWidth,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
class MessageReactionPicker extends StatelessWidget {
|
||||
final ChatController chatController;
|
||||
const MessageReactionPicker({
|
||||
super.key,
|
||||
required this.chatController,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (chatController.selectedEvents.length != 1) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final theme = Theme.of(context);
|
||||
final sentReactions = <String>{};
|
||||
final event = chatController.selectedEvents.first;
|
||||
sentReactions.addAll(
|
||||
event
|
||||
.aggregatedEvents(
|
||||
chatController.timeline!,
|
||||
RelationshipTypes.reaction,
|
||||
)
|
||||
.where(
|
||||
(event) =>
|
||||
event.senderId == event.room.client.userID &&
|
||||
event.type == 'm.reaction',
|
||||
)
|
||||
.map(
|
||||
(event) => event.content
|
||||
.tryGetMap<String, Object?>('m.relates_to')
|
||||
?.tryGet<String>('key'),
|
||||
)
|
||||
.whereType<String>(),
|
||||
);
|
||||
|
||||
return Material(
|
||||
elevation: 4,
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
shadowColor: theme.colorScheme.surface.withAlpha(128),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
...AppConfig.defaultReactions.map(
|
||||
(emoji) => IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
icon: Center(
|
||||
child: Opacity(
|
||||
opacity: sentReactions.contains(
|
||||
emoji,
|
||||
)
|
||||
? 0.33
|
||||
: 1,
|
||||
child: Text(
|
||||
emoji,
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: sentReactions.contains(emoji)
|
||||
? null
|
||||
: () => event.room.sendReaction(
|
||||
event.eventId,
|
||||
emoji,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.add_reaction_outlined,
|
||||
),
|
||||
tooltip: L10n.of(context).customReaction,
|
||||
onPressed: () async {
|
||||
// final emoji = await showAdaptiveBottomSheet<String>(
|
||||
// context: context,
|
||||
// builder: (context) => Scaffold(
|
||||
// appBar: AppBar(
|
||||
// title: Text(
|
||||
// L10n.of(context).customReaction,
|
||||
// ),
|
||||
// leading: CloseButton(
|
||||
// onPressed: () => Navigator.of(
|
||||
// context,
|
||||
// ).pop(
|
||||
// null,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// body: SizedBox(
|
||||
// height: double.infinity,
|
||||
// child: EmojiPicker(
|
||||
// onEmojiSelected: (
|
||||
// _,
|
||||
// emoji,
|
||||
// ) =>
|
||||
// Navigator.of(
|
||||
// context,
|
||||
// ).pop(
|
||||
// emoji.emoji,
|
||||
// ),
|
||||
// config: Config(
|
||||
// emojiViewConfig: const EmojiViewConfig(
|
||||
// backgroundColor: Colors.transparent,
|
||||
// ),
|
||||
// bottomActionBarConfig: const BottomActionBarConfig(
|
||||
// enabled: false,
|
||||
// ),
|
||||
// categoryViewConfig: CategoryViewConfig(
|
||||
// initCategory: Category.SMILEYS,
|
||||
// backspaceColor: theme.colorScheme.primary,
|
||||
// iconColor: theme.colorScheme.primary.withAlpha(
|
||||
// 128,
|
||||
// ),
|
||||
// iconColorSelected: theme.colorScheme.primary,
|
||||
// indicatorColor: theme.colorScheme.primary,
|
||||
// backgroundColor: theme.colorScheme.surface,
|
||||
// ),
|
||||
// skinToneConfig: SkinToneConfig(
|
||||
// dialogBackgroundColor: Color.lerp(
|
||||
// theme.colorScheme.surface,
|
||||
// theme.colorScheme.primaryContainer,
|
||||
// 0.75,
|
||||
// )!,
|
||||
// indicatorColor: theme.colorScheme.onSurface,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// if (emoji == null) {
|
||||
// return;
|
||||
// }
|
||||
// if (sentReactions.contains(
|
||||
// emoji,
|
||||
// )) {
|
||||
// return;
|
||||
// }
|
||||
// onSelect(event);
|
||||
|
||||
// await event.room.sendReaction(
|
||||
// event.eventId,
|
||||
// emoji,
|
||||
// );
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import 'package:matrix/matrix.dart';
|
|||
|
||||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pages/chat/events/message_reactions.dart';
|
||||
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/enums/reading_assistance_mode_enum.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/measure_render_box.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
|
||||
|
|
@ -15,7 +14,6 @@ class OverlayCenterContent extends StatelessWidget {
|
|||
final Event event;
|
||||
final Event? nextEvent;
|
||||
final Event? prevEvent;
|
||||
final PangeaMessageEvent? pangeaMessageEvent;
|
||||
|
||||
final MessageOverlayController overlayController;
|
||||
final ChatController chatController;
|
||||
|
|
@ -25,8 +23,6 @@ class OverlayCenterContent extends StatelessWidget {
|
|||
|
||||
final double? messageHeight;
|
||||
final double? messageWidth;
|
||||
final double maxWidth;
|
||||
final double maxHeight;
|
||||
|
||||
final bool hasReactions;
|
||||
|
||||
|
|
@ -37,11 +33,8 @@ class OverlayCenterContent extends StatelessWidget {
|
|||
required this.event,
|
||||
required this.messageHeight,
|
||||
required this.messageWidth,
|
||||
required this.maxWidth,
|
||||
required this.maxHeight,
|
||||
required this.overlayController,
|
||||
required this.chatController,
|
||||
required this.pangeaMessageEvent,
|
||||
required this.nextEvent,
|
||||
required this.prevEvent,
|
||||
required this.hasReactions,
|
||||
|
|
@ -58,7 +51,7 @@ class OverlayCenterContent extends StatelessWidget {
|
|||
ignoring: !isTransitionAnimation &&
|
||||
readingAssistanceMode != ReadingAssistanceMode.practiceMode,
|
||||
child: Container(
|
||||
constraints: BoxConstraints(maxWidth: maxWidth),
|
||||
constraints: BoxConstraints(maxWidth: overlayController.maxWidth),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Column(
|
||||
|
|
@ -76,7 +69,6 @@ class OverlayCenterContent extends StatelessWidget {
|
|||
.key
|
||||
: null,
|
||||
event,
|
||||
pangeaMessageEvent: pangeaMessageEvent,
|
||||
immersionMode: chatController.choreographer.immersionMode,
|
||||
controller: chatController,
|
||||
overlayController: overlayController,
|
||||
|
|
@ -93,7 +85,6 @@ class OverlayCenterContent extends StatelessWidget {
|
|||
(sizeAnimation == null && isTransitionAnimation)
|
||||
? messageHeight
|
||||
: null,
|
||||
maxHeight: maxHeight,
|
||||
isTransitionAnimation: isTransitionAnimation,
|
||||
readingAssistanceMode: readingAssistanceMode,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
|
@ -12,7 +11,6 @@ import 'package:fluffychat/pages/chat/chat.dart';
|
|||
import 'package:fluffychat/pages/chat/events/message_content.dart';
|
||||
import 'package:fluffychat/pages/chat/events/reply_content.dart';
|
||||
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
|
||||
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart';
|
||||
|
|
@ -28,7 +26,6 @@ import 'package:fluffychat/widgets/matrix.dart';
|
|||
// @ggurdin be great to explain the need/function of a widget like this
|
||||
class OverlayMessage extends StatelessWidget {
|
||||
final Event event;
|
||||
final PangeaMessageEvent? pangeaMessageEvent;
|
||||
final MessageOverlayController overlayController;
|
||||
final ChatController controller;
|
||||
final Event? nextEvent;
|
||||
|
|
@ -39,7 +36,6 @@ class OverlayMessage extends StatelessWidget {
|
|||
final Animation<Size>? sizeAnimation;
|
||||
final double? messageWidth;
|
||||
final double? messageHeight;
|
||||
final double maxHeight;
|
||||
|
||||
final bool isTransitionAnimation;
|
||||
final ReadingAssistanceMode? readingAssistanceMode;
|
||||
|
|
@ -52,8 +48,6 @@ class OverlayMessage extends StatelessWidget {
|
|||
required this.timeline,
|
||||
required this.messageWidth,
|
||||
required this.messageHeight,
|
||||
required this.maxHeight,
|
||||
this.pangeaMessageEvent,
|
||||
this.nextEvent,
|
||||
this.previousEvent,
|
||||
this.sizeAnimation,
|
||||
|
|
@ -146,7 +140,8 @@ class OverlayMessage extends StatelessWidget {
|
|||
final showTranslation = overlayController.showTranslation &&
|
||||
overlayController.translation != null;
|
||||
|
||||
final showTranscription = pangeaMessageEvent?.isAudioMessage == true;
|
||||
final showTranscription =
|
||||
overlayController.pangeaMessageEvent?.isAudioMessage == true;
|
||||
|
||||
final showSpeechTranslation = overlayController.showSpeechTranslation &&
|
||||
overlayController.speechTranslation != null;
|
||||
|
|
@ -284,115 +279,109 @@ class OverlayMessage extends StatelessWidget {
|
|||
),
|
||||
width: messageWidth,
|
||||
height: messageHeight,
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: maxHeight,
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
dragStartBehavior: DragStartBehavior.down,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
if (event.relationshipType == RelationshipTypes.reply)
|
||||
FutureBuilder<Event?>(
|
||||
future: event.getReplyEvent(
|
||||
timeline,
|
||||
),
|
||||
builder: (
|
||||
BuildContext context,
|
||||
snapshot,
|
||||
) {
|
||||
final replyEvent = snapshot.hasData
|
||||
? snapshot.data!
|
||||
: Event(
|
||||
eventId: event.relationshipEventId!,
|
||||
content: {
|
||||
'msgtype': 'm.text',
|
||||
'body': '...',
|
||||
},
|
||||
senderId: "",
|
||||
type: 'm.room.message',
|
||||
room: event.room,
|
||||
status: EventStatus.sent,
|
||||
originServerTs: DateTime.now(),
|
||||
);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 8,
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
if (event.relationshipType == RelationshipTypes.reply)
|
||||
FutureBuilder<Event?>(
|
||||
future: event.getReplyEvent(
|
||||
timeline,
|
||||
),
|
||||
builder: (
|
||||
BuildContext context,
|
||||
snapshot,
|
||||
) {
|
||||
final replyEvent = snapshot.hasData
|
||||
? snapshot.data!
|
||||
: Event(
|
||||
eventId: event.relationshipEventId!,
|
||||
content: {
|
||||
'msgtype': 'm.text',
|
||||
'body': '...',
|
||||
},
|
||||
senderId: "",
|
||||
type: 'm.room.message',
|
||||
room: event.room,
|
||||
status: EventStatus.sent,
|
||||
originServerTs: DateTime.now(),
|
||||
);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 8,
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
borderRadius: ReplyContent.borderRadius,
|
||||
child: InkWell(
|
||||
borderRadius: ReplyContent.borderRadius,
|
||||
child: InkWell(
|
||||
borderRadius: ReplyContent.borderRadius,
|
||||
onTap: () => controller.scrollToEventId(
|
||||
replyEvent.eventId,
|
||||
),
|
||||
child: AbsorbPointer(
|
||||
child: ReplyContent(
|
||||
replyEvent,
|
||||
ownMessage: ownMessage,
|
||||
timeline: timeline,
|
||||
),
|
||||
onTap: () => controller.scrollToEventId(
|
||||
replyEvent.eventId,
|
||||
),
|
||||
child: AbsorbPointer(
|
||||
child: ReplyContent(
|
||||
replyEvent,
|
||||
ownMessage: ownMessage,
|
||||
timeline: timeline,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
MessageContent(
|
||||
displayEvent,
|
||||
textColor: textColor,
|
||||
linkColor: linkColor,
|
||||
borderRadius: borderRadius,
|
||||
timeline: timeline,
|
||||
pangeaMessageEvent: pangeaMessageEvent,
|
||||
immersionMode: immersionMode,
|
||||
overlayController: overlayController,
|
||||
controller: controller,
|
||||
nextEvent: nextEvent,
|
||||
prevEvent: previousEvent,
|
||||
isTransitionAnimation: isTransitionAnimation,
|
||||
readingAssistanceMode: readingAssistanceMode,
|
||||
selected: true,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (event.hasAggregatedEvents(
|
||||
timeline,
|
||||
RelationshipTypes.edit,
|
||||
))
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 8.0,
|
||||
left: 16.0,
|
||||
right: 16.0,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 4.0,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.edit_outlined,
|
||||
color: textColor.withAlpha(164),
|
||||
size: 14,
|
||||
),
|
||||
Text(
|
||||
displayEvent.originServerTs.localizedTimeShort(
|
||||
context,
|
||||
),
|
||||
style: TextStyle(
|
||||
color: textColor.withAlpha(
|
||||
164,
|
||||
),
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
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,
|
||||
RelationshipTypes.edit,
|
||||
))
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 8.0,
|
||||
left: 16.0,
|
||||
right: 16.0,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 4.0,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.edit_outlined,
|
||||
color: textColor.withAlpha(164),
|
||||
size: 14,
|
||||
),
|
||||
Text(
|
||||
displayEvent.originServerTs.localizedTimeShort(
|
||||
context,
|
||||
),
|
||||
style: TextStyle(
|
||||
color: textColor.withAlpha(
|
||||
164,
|
||||
),
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
|
|
@ -404,9 +393,8 @@ class OverlayMessage extends StatelessWidget {
|
|||
color: noBubble ? Colors.transparent : color,
|
||||
borderRadius: borderRadius,
|
||||
),
|
||||
constraints: BoxConstraints(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.columnWidth * 1.5,
|
||||
maxHeight: maxHeight,
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
|
|
|
|||
|
|
@ -147,14 +147,13 @@ class ReadingAssistanceContentState extends State<ReadingAssistanceContent> {
|
|||
),
|
||||
),
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: AppConfig.toolbarMaxHeight,
|
||||
minWidth: min(
|
||||
AppConfig.toolbarMinWidth,
|
||||
widget.overlayController.maxWidth,
|
||||
),
|
||||
minHeight: AppConfig.toolbarMinHeight,
|
||||
maxWidth: widget.overlayController.maxWidth,
|
||||
),
|
||||
height: AppConfig.toolbarMaxHeight,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
|
|
|||
|
|
@ -11,12 +11,13 @@ import 'package:path_provider/path_provider.dart';
|
|||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pages/chat/events/audio_player.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/pressable_button.dart';
|
||||
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart';
|
||||
import 'package:fluffychat/pangea/events/models/representation_content_model.dart';
|
||||
import 'package:fluffychat/pangea/events/utils/report_message.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_audio_card.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
|
||||
|
|
@ -45,13 +46,74 @@ enum SelectMode {
|
|||
}
|
||||
}
|
||||
|
||||
enum MessageActions {
|
||||
reply,
|
||||
forward,
|
||||
edit,
|
||||
delete,
|
||||
copy,
|
||||
download,
|
||||
pin,
|
||||
report,
|
||||
info;
|
||||
|
||||
IconData get icon {
|
||||
switch (this) {
|
||||
case MessageActions.reply:
|
||||
return Icons.reply_all;
|
||||
case MessageActions.forward:
|
||||
return Symbols.forward;
|
||||
case MessageActions.edit:
|
||||
return Symbols.edit;
|
||||
case MessageActions.delete:
|
||||
return Symbols.delete;
|
||||
case MessageActions.copy:
|
||||
return Icons.copy_outlined;
|
||||
case MessageActions.download:
|
||||
return Symbols.download;
|
||||
case MessageActions.pin:
|
||||
return Symbols.push_pin;
|
||||
case MessageActions.report:
|
||||
return Icons.shield_outlined;
|
||||
case MessageActions.info:
|
||||
return Icons.info_outlined;
|
||||
}
|
||||
}
|
||||
|
||||
String tooltip(BuildContext context) {
|
||||
final l10n = L10n.of(context);
|
||||
switch (this) {
|
||||
case MessageActions.reply:
|
||||
return l10n.reply;
|
||||
case MessageActions.forward:
|
||||
return l10n.forward;
|
||||
case MessageActions.edit:
|
||||
return l10n.edit;
|
||||
case MessageActions.delete:
|
||||
return l10n.redactMessage;
|
||||
case MessageActions.copy:
|
||||
return l10n.copy;
|
||||
case MessageActions.download:
|
||||
return l10n.download;
|
||||
case MessageActions.pin:
|
||||
return l10n.pinMessage;
|
||||
case MessageActions.report:
|
||||
return l10n.reportMessage;
|
||||
case MessageActions.info:
|
||||
return l10n.messageInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SelectModeButtons extends StatefulWidget {
|
||||
final VoidCallback lauchPractice;
|
||||
final MessageOverlayController overlayController;
|
||||
final ChatController controller;
|
||||
|
||||
const SelectModeButtons({
|
||||
required this.lauchPractice,
|
||||
required this.overlayController,
|
||||
required this.controller,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
|
@ -475,46 +537,172 @@ class SelectModeButtonsState extends State<SelectModeButtons> {
|
|||
);
|
||||
}
|
||||
|
||||
bool _messageActionEnabled(MessageActions action) {
|
||||
if (messageEvent == null) return false;
|
||||
|
||||
switch (action) {
|
||||
case MessageActions.reply:
|
||||
return widget.controller.selectedEvents.length == 1 &&
|
||||
widget.controller.room.canSendDefaultMessages;
|
||||
case MessageActions.edit:
|
||||
return widget.controller.canEditSelectedEvents &&
|
||||
!widget.controller.selectedEvents.first.isActivityMessage;
|
||||
case MessageActions.delete:
|
||||
return widget.controller.canRedactSelectedEvents;
|
||||
case MessageActions.copy:
|
||||
return widget.controller.selectedEvents.length == 1 &&
|
||||
widget.controller.selectedEvents.single.messageType ==
|
||||
MessageTypes.Text;
|
||||
case MessageActions.download:
|
||||
return widget.controller.canSaveSelectedEvent;
|
||||
case MessageActions.pin:
|
||||
return widget.controller.canPinSelectedEvents;
|
||||
case MessageActions.forward:
|
||||
case MessageActions.report:
|
||||
case MessageActions.info:
|
||||
return widget.controller.selectedEvents.length == 1;
|
||||
}
|
||||
}
|
||||
|
||||
void _onActionPressed(MessageActions action) {
|
||||
switch (action) {
|
||||
case MessageActions.reply:
|
||||
widget.controller.replyAction();
|
||||
break;
|
||||
case MessageActions.forward:
|
||||
widget.controller.forwardEventsAction();
|
||||
break;
|
||||
case MessageActions.edit:
|
||||
widget.controller.editSelectedEventAction();
|
||||
break;
|
||||
case MessageActions.delete:
|
||||
widget.controller.redactEventsAction();
|
||||
break;
|
||||
case MessageActions.copy:
|
||||
widget.controller.copyEventsAction();
|
||||
break;
|
||||
case MessageActions.download:
|
||||
widget.controller.saveSelectedEvent(context);
|
||||
break;
|
||||
case MessageActions.pin:
|
||||
widget.controller.pinEvent();
|
||||
break;
|
||||
case MessageActions.report:
|
||||
final event = widget.controller.selectedEvents.first;
|
||||
widget.controller.clearSelectedEvents();
|
||||
reportEvent(
|
||||
event,
|
||||
widget.controller,
|
||||
widget.controller.context,
|
||||
);
|
||||
break;
|
||||
case MessageActions.info:
|
||||
widget.controller.showEventInfo();
|
||||
widget.controller.clearSelectedEvents();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final modes = messageEvent?.isAudioMessage == true ? audioModes : textModes;
|
||||
final actions = MessageActions.values.where(_messageActionEnabled);
|
||||
|
||||
return Container(
|
||||
height: AppConfig.toolbarButtonsHeight,
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 4.0,
|
||||
children: [
|
||||
for (final mode in modes)
|
||||
TooltipVisibility(
|
||||
visible: (!_isError || mode != _selectedMode),
|
||||
child: Tooltip(
|
||||
message: mode.tooltip(context),
|
||||
child: PressableButton(
|
||||
depressed: mode == _selectedMode,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
onPressed: () => _updateMode(mode),
|
||||
playSound: mode != SelectMode.audio,
|
||||
colorFactor: Theme.of(context).brightness == Brightness.light
|
||||
? 0.55
|
||||
: 0.3,
|
||||
child: Container(
|
||||
height: buttonSize,
|
||||
width: buttonSize,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: icon(mode),
|
||||
),
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Container(
|
||||
width: 250,
|
||||
constraints: const BoxConstraints(
|
||||
maxHeight: AppConfig.toolbarMenuHeight,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: modes.length + actions.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
if (index < modes.length) {
|
||||
final mode = modes[index];
|
||||
return SizedBox(
|
||||
height: 50.0,
|
||||
child: ListTile(
|
||||
leading: Icon(mode.icon),
|
||||
title: Text(mode.tooltip(context)),
|
||||
onTap: () => _updateMode(mode),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else if (index == modes.length) {
|
||||
return const Divider(height: 1.0);
|
||||
} else {
|
||||
final action = actions.elementAt(index - modes.length - 1);
|
||||
return SizedBox(
|
||||
height: 50.0,
|
||||
child: ListTile(
|
||||
leading: Icon(action.icon),
|
||||
title: Text(action.tooltip(context)),
|
||||
onTap: () => _onActionPressed(action),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// return SizedBox(
|
||||
// width: 150,
|
||||
// child: ListView.builder(
|
||||
// itemCount: modes.length,
|
||||
// itemBuilder: (context, index) {
|
||||
// final mode = modes[index];
|
||||
// return ListTile(
|
||||
// leading: Icon(mode.icon),
|
||||
// title: Text(mode.name),
|
||||
// onTap: () {
|
||||
// _updateMode(mode);
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
|
||||
// return Row(
|
||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// spacing: 4.0,
|
||||
// children: [
|
||||
// for (final mode in modes)
|
||||
// TooltipVisibility(
|
||||
// visible: (!_isError || mode != _selectedMode),
|
||||
// child: Tooltip(
|
||||
// message: mode.tooltip(context),
|
||||
// child: PressableButton(
|
||||
// depressed: mode == _selectedMode,
|
||||
// borderRadius: BorderRadius.circular(20),
|
||||
// color: Theme.of(context).colorScheme.primaryContainer,
|
||||
// onPressed: () => _updateMode(mode),
|
||||
// playSound: mode != SelectMode.audio,
|
||||
// colorFactor: Theme.of(context).brightness == Brightness.light
|
||||
// ? 0.55
|
||||
// : 0.3,
|
||||
// child: Container(
|
||||
// height: buttonSize,
|
||||
// width: buttonSize,
|
||||
// decoration: BoxDecoration(
|
||||
// color: Theme.of(context).colorScheme.primaryContainer,
|
||||
// shape: BoxShape.circle,
|
||||
// ),
|
||||
// child: icon(mode),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue