implemented in-chat search with text highlighting, added search navigation UI, and integrated scrollable list for message jumping
This commit is contained in:
@@ -18,6 +18,9 @@ import 'bubble.dart';
|
||||
import 'chat_bubble_poll.dart';
|
||||
import 'chat_bubble_reactions.dart';
|
||||
import 'chat_message_options_dialog.dart';
|
||||
import 'highlighted_linkify.dart';
|
||||
|
||||
enum SearchHighlight { none, secondary, active }
|
||||
|
||||
class ChatBubble extends StatefulWidget {
|
||||
final BuildContext context;
|
||||
@@ -33,6 +36,9 @@ class ChatBubble extends StatefulWidget {
|
||||
|
||||
final void Function({bool renew}) refetch;
|
||||
|
||||
final String? highlightQuery;
|
||||
final SearchHighlight matchHighlight;
|
||||
|
||||
const ChatBubble({
|
||||
required this.context,
|
||||
required this.isSender,
|
||||
@@ -41,6 +47,8 @@ class ChatBubble extends StatefulWidget {
|
||||
required this.refetch,
|
||||
this.isRead = false,
|
||||
this.selfId,
|
||||
this.highlightQuery,
|
||||
this.matchHighlight = SearchHighlight.none,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@@ -142,13 +150,29 @@ class _ChatBubbleState extends State<ChatBubble>
|
||||
|
||||
BubbleStyle _getStyle() {
|
||||
final styles = ChatBubbleStyles(context);
|
||||
final BubbleStyle base;
|
||||
if (widget.bubbleData.messageType !=
|
||||
GetRoomResponseObjectMessageType.comment) {
|
||||
return styles.getSystemStyle();
|
||||
base = styles.getSystemStyle();
|
||||
} else {
|
||||
base = widget.isSender
|
||||
? styles.getSelfStyle(false)
|
||||
: styles.getRemoteStyle(false);
|
||||
}
|
||||
switch (widget.matchHighlight) {
|
||||
case SearchHighlight.none:
|
||||
return base;
|
||||
case SearchHighlight.secondary:
|
||||
return base.copyWith(
|
||||
borderWidth: 1.5,
|
||||
borderColor: Theme.of(context).colorScheme.primary.withValues(alpha: 0.45),
|
||||
);
|
||||
case SearchHighlight.active:
|
||||
return base.copyWith(
|
||||
borderWidth: 3,
|
||||
borderColor: Theme.of(context).colorScheme.primary,
|
||||
);
|
||||
}
|
||||
return widget.isSender
|
||||
? styles.getSelfStyle(false)
|
||||
: styles.getRemoteStyle(false);
|
||||
}
|
||||
|
||||
void _showOptionsDialog() => showChatMessageOptionsDialog(
|
||||
@@ -196,15 +220,29 @@ class _ChatBubbleState extends State<ChatBubble>
|
||||
GetRoomResponseObjectMessageType.deletedComment;
|
||||
|
||||
final parent = widget.bubbleData.parent;
|
||||
final actorBaseStyle = TextStyle(
|
||||
color: Theme.of(context).primaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
);
|
||||
final actorText = Text(
|
||||
widget.bubbleData.actorDisplayName,
|
||||
textAlign: TextAlign.start,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: actorBaseStyle,
|
||||
);
|
||||
final actorWidget = (widget.highlightQuery?.trim().isNotEmpty ?? false)
|
||||
? Text.rich(
|
||||
TextSpan(
|
||||
children: buildHighlightedSpans(
|
||||
text: widget.bubbleData.actorDisplayName,
|
||||
query: widget.highlightQuery,
|
||||
baseStyle: actorBaseStyle,
|
||||
),
|
||||
),
|
||||
textAlign: TextAlign.start,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
)
|
||||
: actorText;
|
||||
|
||||
final timeText = Text(
|
||||
DateTime.fromMillisecondsSinceEpoch(
|
||||
@@ -252,8 +290,16 @@ class _ChatBubbleState extends State<ChatBubble>
|
||||
style: _getStyle(),
|
||||
child: _BubbleContent(
|
||||
actorText: actorText,
|
||||
actorWidget: actorWidget,
|
||||
timeText: timeText,
|
||||
messageWidget: message.getWidget(),
|
||||
messageWidget: message.getWidget(
|
||||
highlightQuery: widget.highlightQuery,
|
||||
style:
|
||||
widget.bubbleData.messageType ==
|
||||
GetRoomResponseObjectMessageType.system
|
||||
? Theme.of(context).textTheme.bodySmall
|
||||
: null,
|
||||
),
|
||||
parent: parent,
|
||||
bubbleData: widget.bubbleData,
|
||||
isSender: widget.isSender,
|
||||
@@ -282,6 +328,7 @@ class _ChatBubbleState extends State<ChatBubble>
|
||||
|
||||
class _BubbleContent extends StatelessWidget {
|
||||
final Text actorText;
|
||||
final Widget actorWidget;
|
||||
final Text timeText;
|
||||
final Widget messageWidget;
|
||||
final GetChatResponseObject? parent;
|
||||
@@ -298,6 +345,7 @@ class _BubbleContent extends StatelessWidget {
|
||||
|
||||
const _BubbleContent({
|
||||
required this.actorText,
|
||||
required this.actorWidget,
|
||||
required this.timeText,
|
||||
required this.messageWidget,
|
||||
required this.parent,
|
||||
@@ -323,7 +371,7 @@ class _BubbleContent extends StatelessWidget {
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
if (showActorDisplayName) Positioned(top: 0, left: 0, child: actorText),
|
||||
if (showActorDisplayName) Positioned(top: 0, left: 0, child: actorWidget),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: showBubbleTime ? 18 : 0,
|
||||
|
||||
Reference in New Issue
Block a user