Skip to content

Commit 2de1f1b

Browse files
committed
msglist: In single-conversation view, make recipient headers not tappable.
Fixes: #1171
1 parent 3ff7096 commit 2de1f1b

File tree

2 files changed

+167
-9
lines changed

2 files changed

+167
-9
lines changed

Diff for: lib/widgets/message_list.dart

+18-9
Original file line numberDiff line numberDiff line change
@@ -899,8 +899,9 @@ class RecipientHeader extends StatelessWidget {
899899
final message = this.message;
900900
return switch (message) {
901901
StreamMessage() => StreamMessageRecipientHeader(message: message,
902-
showStream: _containsDifferentChannels(narrow)),
903-
DmMessage() => DmRecipientHeader(message: message),
902+
showStream: _containsDifferentChannels(narrow),
903+
inTopicNarrow: narrow is TopicNarrow),
904+
DmMessage() => DmRecipientHeader(message: message, inDmNarrow: narrow is DmNarrow),
904905
};
905906
}
906907
}
@@ -1015,10 +1016,12 @@ class StreamMessageRecipientHeader extends StatelessWidget {
10151016
super.key,
10161017
required this.message,
10171018
required this.showStream,
1019+
required this.inTopicNarrow,
10181020
});
10191021

10201022
final StreamMessage message;
10211023
final bool showStream;
1024+
final bool inTopicNarrow;
10221025

10231026
@override
10241027
Widget build(BuildContext context) {
@@ -1104,10 +1107,13 @@ class StreamMessageRecipientHeader extends StatelessWidget {
11041107
store.topicVisibilityPolicy(message.streamId, topic))),
11051108
]));
11061109

1110+
// Tap interaction is disabled when already in a topic narrow view to avoid redundant navigation.
1111+
// This behavior may be updated with improved UI patterns in issue #1039.
11071112
return GestureDetector(
1108-
onTap: () => Navigator.push(context,
1109-
MessageListPage.buildRoute(context: context,
1110-
narrow: TopicNarrow.ofMessage(message))),
1113+
onTap: !inTopicNarrow
1114+
? () => Navigator.push(context, MessageListPage.buildRoute(context: context,
1115+
narrow: TopicNarrow.ofMessage(message)))
1116+
: null,
11111117
onLongPress: () => showTopicActionSheet(context,
11121118
channelId: message.streamId, topic: topic),
11131119
child: ColoredBox(
@@ -1126,9 +1132,10 @@ class StreamMessageRecipientHeader extends StatelessWidget {
11261132
}
11271133

11281134
class DmRecipientHeader extends StatelessWidget {
1129-
const DmRecipientHeader({super.key, required this.message});
1135+
const DmRecipientHeader({super.key, required this.message, required this.inDmNarrow});
11301136

11311137
final DmMessage message;
1138+
final bool inDmNarrow;
11321139

11331140
@override
11341141
Widget build(BuildContext context) {
@@ -1148,10 +1155,12 @@ class DmRecipientHeader extends StatelessWidget {
11481155

11491156
final messageListTheme = MessageListTheme.of(context);
11501157

1158+
// Tap interaction is disabled when already in a DM narrow view to avoid redundant navigation.
11511159
return GestureDetector(
1152-
onTap: () => Navigator.push(context,
1153-
MessageListPage.buildRoute(context: context,
1154-
narrow: DmNarrow.ofMessage(message, selfUserId: store.selfUserId))),
1160+
onTap: !inDmNarrow
1161+
? () => Navigator.push(context, MessageListPage.buildRoute(context: context,
1162+
narrow: DmNarrow.ofMessage(message, selfUserId: store.selfUserId)))
1163+
: null,
11551164
child: ColoredBox(
11561165
color: messageListTheme.dmRecipientHeaderBg,
11571166
child: Padding(

Diff for: test/widgets/message_list_test.dart

+149
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,85 @@ void main() {
922922
await tester.pump();
923923
tester.widget(find.text('new stream name'));
924924
});
925+
926+
testWidgets('navigates to TopicNarrow on tapping topic in CombinedFeedNarrow', (tester) async {
927+
final pushedRoutes = <Route<void>>[];
928+
final navObserver = TestNavigatorObserver()
929+
..onPushed = (route, prevRoute) => pushedRoutes.add(route);
930+
931+
final stream = eg.stream(name: 'stream name');
932+
final message = eg.streamMessage(stream: stream, topic: 'topic name');
933+
934+
await setupMessageListPage(
935+
tester,
936+
narrow: const CombinedFeedNarrow(),
937+
messages: [message],
938+
subscriptions: [eg.subscription(stream)],
939+
navObservers: [navObserver],
940+
);
941+
942+
assert(pushedRoutes.length == 1);
943+
pushedRoutes.clear();
944+
945+
await tester.pump();
946+
check(findInMessageList('stream name')).length.equals(1);
947+
check(findInMessageList('topic name')).length.equals(1);
948+
949+
connection.prepare(json: eg.newestGetMessagesResult(
950+
messages: [message],
951+
foundOldest: true,
952+
).toJson());
953+
954+
final topicFinder = find.descendant(
955+
of: find.byType(StreamMessageRecipientHeader),
956+
matching: find.text('topic name'),
957+
);
958+
959+
check(topicFinder.evaluate()).length.equals(1);
960+
await tester.tap(topicFinder);
961+
await tester.pumpAndSettle();
962+
963+
check(pushedRoutes).length.equals(1);
964+
final pushedRoute = pushedRoutes.first;
965+
check(pushedRoute).isA<WidgetRoute>();
966+
check((pushedRoute as WidgetRoute).page).isA<MessageListPage>();
967+
final messageListPage = pushedRoute.page as MessageListPage;
968+
check(messageListPage.initNarrow).isA<TopicNarrow>();
969+
final topicNarrow = messageListPage.initNarrow as TopicNarrow;
970+
check(topicNarrow.streamId).equals(stream.streamId);
971+
check(topicNarrow.topic).equals('topic name');
972+
});
973+
974+
testWidgets('does not navigate on tapping topic in TopicNarrow', (tester) async {
975+
final pushedRoutes = <Route<void>>[];
976+
final navObserver = TestNavigatorObserver()
977+
..onPushed = (route, prevRoute) => pushedRoutes.add(route);
978+
979+
final channel = eg.stream(name: 'stream name');
980+
final msg = eg.streamMessage(stream: channel, topic: 'topic name');
981+
982+
await setupMessageListPage(
983+
tester,
984+
narrow: TopicNarrow(channel.streamId, 'topic name'),
985+
navObservers: [navObserver],
986+
streams: [channel],
987+
messages: [msg],
988+
);
989+
990+
assert(pushedRoutes.length == 1);
991+
pushedRoutes.clear();
992+
993+
final topicFinder = find.descendant(
994+
of: find.byType(StreamMessageRecipientHeader),
995+
matching: find.text('topic name'),
996+
);
997+
998+
check(topicFinder.evaluate()).length.equals(1);
999+
await tester.tap(topicFinder);
1000+
await tester.pumpAndSettle();
1001+
1002+
check(pushedRoutes).length.equals(0);
1003+
});
9251004
});
9261005

9271006
group('DmRecipientHeader', () {
@@ -987,6 +1066,76 @@ void main() {
9871066
tester.widget(find.textContaining(RegExp("Dec 1[89], 2022")));
9881067
tester.widget(find.textContaining(RegExp("Aug 2[23], 2022")));
9891068
});
1069+
1070+
testWidgets('navigates to DmNarrow on tapping recipient header in CombinedFeedNarrow', (tester) async {
1071+
final pushedRoutes = <Route<void>>[];
1072+
final navObserver = TestNavigatorObserver()
1073+
..onPushed = (route, prevRoute) {
1074+
pushedRoutes.add(route);
1075+
};
1076+
1077+
final dmMessage = eg.dmMessage(from: eg.selfUser, to: [eg.otherUser]);
1078+
1079+
await setupMessageListPage(
1080+
tester,
1081+
narrow: const CombinedFeedNarrow(),
1082+
messages: [dmMessage],
1083+
navObservers: [navObserver],
1084+
);
1085+
1086+
assert(pushedRoutes.length == 1);
1087+
pushedRoutes.clear();
1088+
1089+
await tester.pumpAndSettle();
1090+
1091+
connection.prepare(json: eg.newestGetMessagesResult(
1092+
messages: [dmMessage],
1093+
foundOldest: true,
1094+
).toJson());
1095+
1096+
final recipientHeaderFinder = find.byType(DmRecipientHeader);
1097+
check(recipientHeaderFinder.evaluate()).length.equals(1);
1098+
1099+
await tester.tap(recipientHeaderFinder);
1100+
await tester.pumpAndSettle();
1101+
1102+
check(pushedRoutes).length.equals(1);
1103+
1104+
final pushedRoute = pushedRoutes.first;
1105+
check(pushedRoute).isA<WidgetRoute>();
1106+
check((pushedRoute as WidgetRoute).page).isA<MessageListPage>();
1107+
final messageListPage = pushedRoute.page as MessageListPage;
1108+
check(messageListPage.initNarrow).isA<DmNarrow>();
1109+
final dmNarrow = messageListPage.initNarrow as DmNarrow;
1110+
check(dmNarrow.otherRecipientIds).contains(eg.otherUser.userId);
1111+
});
1112+
1113+
testWidgets('does not navigate on tapping recipient header in DmNarrow', (tester) async {
1114+
final pushedRoutes = <Route<void>>[];
1115+
final navObserver = TestNavigatorObserver()
1116+
..onPushed = (route, prevRoute) => pushedRoutes.add(route);
1117+
1118+
final dmMessage = eg.dmMessage(from: eg.selfUser, to: [eg.otherUser]);
1119+
1120+
await setupMessageListPage(
1121+
tester,
1122+
narrow: DmNarrow.withUser(eg.otherUser.userId, selfUserId: eg.selfUser.userId),
1123+
navObservers: [navObserver],
1124+
messages: [dmMessage],
1125+
);
1126+
1127+
assert(pushedRoutes.length == 1);
1128+
pushedRoutes.clear();
1129+
1130+
final recipientHeaderFinder = find.byType(DmRecipientHeader);
1131+
1132+
check(recipientHeaderFinder.evaluate()).length.equals(1);
1133+
1134+
await tester.tap(recipientHeaderFinder);
1135+
await tester.pumpAndSettle();
1136+
1137+
check(pushedRoutes).length.equals(0);
1138+
});
9901139
});
9911140

9921141
group('formatHeaderDate', () {

0 commit comments

Comments
 (0)