Skip to content

Commit ff98235

Browse files
committed
feat: make @-mentions tappable to navigate to user profiles
- Add userId property to UserMentionNode to store user ID from data-user-id HTML attribute - Extract user ID during @-mention parsing from HTML data-user-id attribute - Modify UserMention widget to handle taps and navigate to ProfilePage when valid user ID exists - Add import for ProfilePage to enable navigation functionality - Maintain existing styling and behavior while adding tappable functionality
1 parent 6adc9c3 commit ff98235

File tree

2 files changed

+52
-20
lines changed

2 files changed

+52
-20
lines changed

lib/model/content.dart

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -928,8 +928,11 @@ class UserMentionNode extends InlineContainerNode {
928928
const UserMentionNode({
929929
super.debugHtmlNode,
930930
required super.nodes,
931+
this.userId,
931932
});
932933

934+
final int? userId;
935+
933936
// For the legacy design, we don't need this information in code; instead,
934937
// the inner text already shows how to communicate it to the user
935938
// (e.g., silent mentions' text lacks a leading "@"),
@@ -1083,7 +1086,15 @@ class _ZulipInlineContentParser {
10831086
// either a debug-mode check, or perhaps we can make expectations much
10841087
// tighter on a UserMentionNode's contents overall.
10851088
final nodes = parseInlineContentList(element.nodes);
1086-
return UserMentionNode(nodes: nodes, debugHtmlNode: debugHtmlNode);
1089+
1090+
// Extract user ID from data-user-id attribute if present
1091+
int? userId;
1092+
final userIdStr = element.attributes['data-user-id'];
1093+
if (userIdStr != null) {
1094+
userId = int.tryParse(userIdStr);
1095+
}
1096+
1097+
return UserMentionNode(nodes: nodes, debugHtmlNode: debugHtmlNode, userId: userId);
10871098
}
10881099

10891100
/// The links found so far in the current block inline container.

lib/widgets/content.dart

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import 'katex.dart';
2121
import 'lightbox.dart';
2222
import 'message_list.dart';
2323
import 'poll.dart';
24+
import 'profile.dart';
2425
import 'scrolling.dart';
2526
import 'store.dart';
2627
import 'text.dart';
@@ -1216,25 +1217,45 @@ class UserMention extends StatelessWidget {
12161217
@override
12171218
Widget build(BuildContext context) {
12181219
final contentTheme = ContentTheme.of(context);
1219-
return Container(
1220-
decoration: BoxDecoration(
1221-
// TODO(#646) different for wildcard mentions
1222-
color: contentTheme.colorDirectMentionBackground,
1223-
borderRadius: const BorderRadius.all(Radius.circular(3))),
1224-
padding: const EdgeInsets.symmetric(horizontal: 0.2 * kBaseFontSize),
1225-
child: InlineContent(
1226-
// If an @-mention is inside a link, let the @-mention override it.
1227-
recognizer: null, // TODO(#1867) make @-mentions tappable, for info on user
1228-
// One hopes an @-mention can't contain an embedded link.
1229-
// (The parser on creating a UserMentionNode has a TODO to check that.)
1230-
linkRecognizers: null,
1231-
1232-
// TODO(#647) when self-user is non-silently mentioned, make bold, and:
1233-
// TODO(#646) when self-user is non-silently mentioned,
1234-
// distinguish font color between direct and wildcard mentions
1235-
style: ambientTextStyle,
1236-
1237-
nodes: node.nodes));
1220+
final userId = node.userId;
1221+
1222+
final innerContent = InlineContent(
1223+
// If an @-mention is inside a link, let the @-mention override it.
1224+
recognizer: null,
1225+
// One hopes an @-mention can't contain an embedded link.
1226+
// (The parser on creating a UserMentionNode has a TODO to check that.)
1227+
linkRecognizers: null,
1228+
1229+
style: ambientTextStyle,
1230+
1231+
nodes: node.nodes);
1232+
1233+
if (userId != null && userId > 0) {
1234+
// Wrap with gesture detector if we have a valid user ID
1235+
return GestureDetector(
1236+
onTap: () => Navigator.push(
1237+
context,
1238+
ProfilePage.buildRoute(context: context, userId: userId),
1239+
),
1240+
child: Container(
1241+
decoration: BoxDecoration(
1242+
// TODO(#646) different for wildcard mentions
1243+
color: contentTheme.colorDirectMentionBackground,
1244+
borderRadius: const BorderRadius.all(Radius.circular(3))),
1245+
padding: const EdgeInsets.symmetric(horizontal: 0.2 * kBaseFontSize),
1246+
child: innerContent,
1247+
),
1248+
);
1249+
} else {
1250+
// Regular container without gesture detector if no valid user ID
1251+
return Container(
1252+
decoration: BoxDecoration(
1253+
// TODO(#646) different for wildcard mentions
1254+
color: contentTheme.colorDirectMentionBackground,
1255+
borderRadius: const BorderRadius.all(Radius.circular(3))),
1256+
padding: const EdgeInsets.symmetric(horizontal: 0.2 * kBaseFontSize),
1257+
child: innerContent);
1258+
}
12381259
}
12391260

12401261
// This is a more literal translation of Zulip web's CSS.

0 commit comments

Comments
 (0)