Skip to content

Conversation

@loveucifer
Copy link

@loveucifer loveucifer commented Oct 7, 2025

mentions: Enable navigation to user profiles on tap on chat threads

Before, tapping on an @-mention in a message had no effect. This
was inconsistent with tapping a sender's name or avatar, which
navigates to their profile in the thread . This change makes @-mentions tappable to
provide a consistent user experience and match web functionality as mentioned in the issue

The HTML parser extracts the data-user-id" attribute from the mention's element and stores it in theUserMentionNode. The UserMentionwidget uses this "id" to wrap the text in aGestureDetector`,
navigating to the user's profile page on tap.

Working Demo

zulip.mp4

Fixes : #1867

@loveucifer loveucifer marked this pull request as ready for review October 7, 2025 01:15
@gnprice
Copy link
Member

gnprice commented Oct 7, 2025

Thanks. Before we can review this PR, it will need a test, as described in the general instructions in the repo's README.

@gnprice gnprice marked this pull request as draft October 7, 2025 18:59
@loveucifer loveucifer force-pushed the feature/tappable-mentions branch from ff98235 to 0722254 Compare October 8, 2025 12:38
@loveucifer loveucifer marked this pull request as ready for review October 8, 2025 12:44
@loveucifer loveucifer closed this Oct 8, 2025
@loveucifer loveucifer reopened this Oct 8, 2025
@loveucifer loveucifer marked this pull request as draft October 8, 2025 13:00
@loveucifer loveucifer force-pushed the feature/tappable-mentions branch from 0722254 to 0194098 Compare October 8, 2025 13:44
@loveucifer loveucifer changed the title feat: make @-mentions tappable to navigate to user profiles mentions : make @-mentions tappable to navigate to user profiles Oct 8, 2025
@loveucifer loveucifer marked this pull request as ready for review October 8, 2025 14:04
@gnprice gnprice added the maintainer review PR ready for review by Zulip maintainers label Oct 8, 2025
Copy link
Member

@rajveermalviya rajveermalviya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @loveucifer! Sorry for delayed intial review. Comments below.

Also please update the tests in test/model/content_test.dart accordingly.

Comment on lines 1220 to 1258
final userId = node.userId;

final innerContent = InlineContent(
// If an @-mention is inside a link, let the @-mention override it.
recognizer: null,
// One hopes an @-mention can't contain an embedded link.
// (The parser on creating a UserMentionNode has a TODO to check that.)
linkRecognizers: null,

style: ambientTextStyle,

nodes: node.nodes);

if (userId != null && userId > 0) {
// Wrap with gesture detector if we have a valid user ID
return GestureDetector(
onTap: () => Navigator.push(
context,
ProfilePage.buildRoute(context: context, userId: userId),
),
child: Container(
decoration: BoxDecoration(
// TODO(#646) different for wildcard mentions
color: contentTheme.colorDirectMentionBackground,
borderRadius: const BorderRadius.all(Radius.circular(3))),
padding: const EdgeInsets.symmetric(horizontal: 0.2 * kBaseFontSize),
child: innerContent,
),
);
} else {
// Regular container without gesture detector if no valid user ID
return Container(
decoration: BoxDecoration(
// TODO(#646) different for wildcard mentions
color: contentTheme.colorDirectMentionBackground,
borderRadius: const BorderRadius.all(Radius.circular(3))),
padding: const EdgeInsets.symmetric(horizontal: 0.2 * kBaseFontSize),
child: innerContent);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, this is doing the same right?

Suggested change
final userId = node.userId;
final innerContent = InlineContent(
// If an @-mention is inside a link, let the @-mention override it.
recognizer: null,
// One hopes an @-mention can't contain an embedded link.
// (The parser on creating a UserMentionNode has a TODO to check that.)
linkRecognizers: null,
style: ambientTextStyle,
nodes: node.nodes);
if (userId != null && userId > 0) {
// Wrap with gesture detector if we have a valid user ID
return GestureDetector(
onTap: () => Navigator.push(
context,
ProfilePage.buildRoute(context: context, userId: userId),
),
child: Container(
decoration: BoxDecoration(
// TODO(#646) different for wildcard mentions
color: contentTheme.colorDirectMentionBackground,
borderRadius: const BorderRadius.all(Radius.circular(3))),
padding: const EdgeInsets.symmetric(horizontal: 0.2 * kBaseFontSize),
child: innerContent,
),
);
} else {
// Regular container without gesture detector if no valid user ID
return Container(
decoration: BoxDecoration(
// TODO(#646) different for wildcard mentions
color: contentTheme.colorDirectMentionBackground,
borderRadius: const BorderRadius.all(Radius.circular(3))),
padding: const EdgeInsets.symmetric(horizontal: 0.2 * kBaseFontSize),
child: innerContent);
}
Widget widget = Container(
decoration: BoxDecoration(
// TODO(#646) different for wildcard mentions
color: contentTheme.colorDirectMentionBackground,
borderRadius: const BorderRadius.all(Radius.circular(3))),
padding: const EdgeInsets.symmetric(horizontal: 0.2 * kBaseFontSize),
child: InlineContent(
// If an @-mention is inside a link, let the @-mention override it.
recognizer: null, // TODO(#1867) make @-mentions tappable, for info on user
// One hopes an @-mention can't contain an embedded link.
// (The parser on creating a UserMentionNode has a TODO to check that.)
linkRecognizers: null,
// TODO(#647) when self-user is non-silently mentioned, make bold, and:
// TODO(#646) when self-user is non-silently mentioned,
// distinguish font color between direct and wildcard mentions
style: ambientTextStyle,
nodes: node.nodes));
if (node.userId == null) return widget;
return GestureDetector(
onTap: () {
Navigator.push(context,
ProfilePage.buildRoute(context: context, userId: node.userId!));
},
child: widget);

- Make userId required in UserMentionNode for explicit parameters
- Reorder constructor parameters
- Refactor UserMention widget to avoid code duplication
- Move tests to content_test.dart
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintainer review PR ready for review by Zulip maintainers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tap an @-mention to go to user's profile page

3 participants