enable toolbar on click for html messages

This commit is contained in:
ggurdin 2024-09-03 16:15:18 -04:00
parent b75abc1c63
commit 11627c2bfb
No known key found for this signature in database
GPG key ID: A01CB41737CBB478
5 changed files with 118 additions and 94 deletions

View file

@ -1,8 +1,11 @@
import 'package:collection/collection.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_highlighter/flutter_highlighter.dart';
import 'package:flutter_highlighter/themes/shades-of-purple.dart';
import 'package:flutter_html/flutter_html.dart';
@ -18,12 +21,22 @@ class HtmlMessage extends StatelessWidget {
final String html;
final Room room;
final Color textColor;
// #Pangea
final bool isOverlay;
final PangeaMessageEvent? pangeaMessageEvent;
final ChatController controller;
// Pangea#
const HtmlMessage({
super.key,
required this.html,
required this.room,
this.textColor = Colors.black,
// #Pangea
required this.isOverlay,
this.pangeaMessageEvent,
required this.controller,
// Pangea#
});
dom.Node _linkifyHtml(dom.Node element) {
@ -76,21 +89,16 @@ class HtmlMessage extends StatelessWidget {
// there is no need to pre-validate the html, as we validate it while rendering
// #Pangea
return MouseRegion(
// onHover: messageToolbar?.onMouseRegionUpdate,
child: SelectionArea(
// onSelectionChanged: (SelectedContent? selection) =>
// messageToolbar?.onTextSelection(
// selectedContent: selection,
// context: context,
// ),
// focusNode: messageToolbar?.focusNode,
// contextMenuBuilder: (context, state) =>
// messageToolbar?.contextMenuOverride(
// context: context,
// contentSelection: state,
// ) ??
// const SizedBox(),
return SelectionArea(
onSelectionChanged: (SelectedContent? selection) {
controller.textSelection.onSelection(selection?.plainText);
},
child: GestureDetector(
onTap: () {
if (pangeaMessageEvent != null && !isOverlay) {
controller.showToolbar(pangeaMessageEvent!);
}
},
// Pangea#
child: Html.fromElement(
documentElement: element as dom.Element,
@ -173,11 +181,6 @@ class HtmlMessage extends StatelessWidget {
),
),
);
// ),
// ],
// ),
// ),
// );
}
static const Set<String> fallbackTextTags = {'tg-forward'};
@ -303,7 +306,6 @@ class ImageExtension extends HtmlExtension {
uri: mxcUrl,
width: width ?? height ?? defaultDimension,
height: height ?? width ?? defaultDimension,
cacheKey: mxcUrl.toString(),
),
),
);

View file

@ -3,13 +3,13 @@ import 'dart:math';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pages/chat/events/video_player.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart';
import 'package:fluffychat/pangea/widgets/igc/pangea_rich_text.dart';
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart';
@ -205,6 +205,11 @@ class MessageContent extends StatelessWidget {
html: html,
textColor: textColor,
room: event.room,
// #Pangea
isOverlay: isOverlay,
controller: controller,
pangeaMessageEvent: pangeaMessageEvent,
// Pangea#
);
}
// else we fall through to the normal message rendering
@ -285,8 +290,8 @@ class MessageContent extends StatelessWidget {
final bigEmotes = event.onlyEmotes &&
event.numberEmotes > 0 &&
event.numberEmotes <= 10;
// #Pangea
// return Linkify(
final messageTextStyle = TextStyle(
overflow: TextOverflow.ellipsis,
color: textColor,
@ -314,38 +319,36 @@ class MessageContent extends StatelessWidget {
),
);
}
// Pangea#
return SelectableLinkify(
onSelectionChanged: (selection, cause) {
if (isOverlay) {
controller.textSelection.onTextSelection(selection);
}
},
onTap: () {
if (pangeaMessageEvent != null && !isOverlay) {
HapticFeedback.mediumImpact();
controller.showToolbar(pangeaMessageEvent!);
}
},
enableInteractiveSelection: isOverlay,
// Pangea#
text: event.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
hideReply: true,
return
// #Pangea
ToolbarSelectionArea(
controller: controller,
pangeaMessageEvent: pangeaMessageEvent,
isOverlay: isOverlay,
child:
// Pangea#
Linkify(
text: event.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
hideReply: true,
),
style: TextStyle(
color: textColor,
fontSize: bigEmotes ? fontSize * 3 : fontSize,
decoration:
event.redacted ? TextDecoration.lineThrough : null,
),
options: const LinkifyOptions(humanize: false),
linkStyle: TextStyle(
color: textColor.withAlpha(150),
fontSize: bigEmotes ? fontSize * 3 : fontSize,
decoration: TextDecoration.underline,
decorationColor: textColor.withAlpha(150),
),
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
),
style: TextStyle(
color: textColor,
fontSize: bigEmotes ? fontSize * 3 : fontSize,
decoration: event.redacted ? TextDecoration.lineThrough : null,
),
options: const LinkifyOptions(humanize: false),
linkStyle: TextStyle(
color: textColor.withAlpha(150),
fontSize: bigEmotes ? fontSize * 3 : fontSize,
decoration: TextDecoration.underline,
decorationColor: textColor.withAlpha(150),
),
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
);
}
case EventTypes.CallInvite:

View file

@ -1,8 +1,5 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
class MessageTextSelection {
String? selectedText;
String messageText = "";
@ -13,23 +10,17 @@ class MessageTextSelection {
messageText = text;
}
void onTextSelection(TextSelection selection) => selection.isCollapsed == true
void onSelection(String? text) => text == null || text.isEmpty
? clearTextSelection()
: setTextSelection(selection);
: setTextSelection(text);
void setTextSelection(TextSelection selection) {
selectedText = selection.textInside(messageText);
if (BrowserContextMenu.enabled && kIsWeb) {
BrowserContextMenu.disableContextMenu();
}
void setTextSelection(String selection) {
selectedText = selection;
selectionStream.add(selectedText);
}
void clearTextSelection() {
selectedText = null;
if (kIsWeb && !BrowserContextMenu.enabled) {
BrowserContextMenu.enableContextMenu();
}
selectionStream.add(selectedText);
}

View file

@ -14,6 +14,7 @@ import 'package:fluffychat/pangea/widgets/igc/word_data_card.dart';
import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class MessageToolbar extends StatefulWidget {
final MessageTextSelection textSelection;
@ -276,3 +277,35 @@ class MessageToolbarState extends State<MessageToolbar> {
);
}
}
class ToolbarSelectionArea extends StatelessWidget {
final ChatController controller;
final PangeaMessageEvent? pangeaMessageEvent;
final bool isOverlay;
final Widget child;
const ToolbarSelectionArea({
required this.controller,
this.pangeaMessageEvent,
this.isOverlay = false,
required this.child,
super.key,
});
@override
Widget build(BuildContext context) {
return SelectionArea(
onSelectionChanged: (SelectedContent? selection) {
controller.textSelection.onSelection(selection?.plainText);
},
child: GestureDetector(
onTap: () {
if (pangeaMessageEvent != null && !isOverlay) {
controller.showToolbar(pangeaMessageEvent!);
}
},
child: child,
),
);
}
}

View file

@ -8,6 +8,7 @@ import 'package:fluffychat/pangea/enum/instructions_enum.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/models/representation_content_model.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -134,37 +135,31 @@ class PangeaRichTextState extends State<PangeaRichText> {
}
//TODO - take out of build function of every message
final Widget richText = SelectableText.rich(
onSelectionChanged: (selection, cause) {
if (widget.isOverlay) {
widget.controller.textSelection.onTextSelection(selection);
}
},
onTap: () {
if (!widget.isOverlay) {
widget.controller.showToolbar(widget.pangeaMessageEvent);
}
},
enableInteractiveSelection: widget.isOverlay,
TextSpan(
text: textSpan,
style: widget.style,
children: [
if (_fetchingRepresentation)
const WidgetSpan(
child: Padding(
padding: EdgeInsets.only(left: 5.0),
child: SizedBox(
height: 14,
width: 14,
child: CircularProgressIndicator(
strokeWidth: 2.0,
color: AppConfig.secondaryColor,
final Widget richText = ToolbarSelectionArea(
isOverlay: widget.isOverlay,
pangeaMessageEvent: widget.pangeaMessageEvent,
controller: widget.controller,
child: RichText(
text: TextSpan(
text: textSpan,
style: widget.style,
children: [
if (_fetchingRepresentation)
const WidgetSpan(
child: Padding(
padding: EdgeInsets.only(left: 5.0),
child: SizedBox(
height: 14,
width: 14,
child: CircularProgressIndicator(
strokeWidth: 2.0,
color: AppConfig.secondaryColor,
),
),
),
),
),
],
],
),
),
);