Skip to content

Commit 1a2633f

Browse files
committed
recent-dms: Distinguish isBot: true users with a bot marker
Fixes: #636
1 parent ee71b42 commit 1a2633f

File tree

2 files changed

+126
-43
lines changed

2 files changed

+126
-43
lines changed

lib/widgets/recent_dm_conversations.dart

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,20 @@ class RecentDmConversationsItem extends StatelessWidget {
9393

9494
final String title;
9595
final Widget avatar;
96+
final bool isBot;
9697
switch (narrow.otherRecipientIds) { // TODO dedupe with DM items in [InboxPage]
9798
case []:
9899
title = selfUser.fullName;
99100
avatar = AvatarImage(userId: selfUser.userId, size: _avatarSize);
101+
isBot = false;
100102
case [var otherUserId]:
101103
// TODO(#296) actually don't show this row if the user is muted?
102104
// (should we offer a "spam folder" style summary screen of recent
103105
// 1:1 DM conversations from muted users?)
104106
final otherUser = store.users[otherUserId];
105107
title = otherUser?.fullName ?? '(unknown user)';
106108
avatar = AvatarImage(userId: otherUserId, size: _avatarSize);
109+
isBot = otherUser?.isBot ?? false;
107110
default:
108111
// TODO(i18n): List formatting, like you can do in JavaScript:
109112
// new Intl.ListFormat('ja').format(['Chris', 'Greg', 'Alya'])
@@ -114,6 +117,7 @@ class RecentDmConversationsItem extends StatelessWidget {
114117
child: Center(
115118
// TODO(#95) need dark-theme color
116119
child: Icon(ZulipIcons.group_dm, color: Colors.black.withOpacity(0.5))));
120+
isBot = false;
117121
}
118122

119123
return Material(
@@ -131,16 +135,27 @@ class RecentDmConversationsItem extends StatelessWidget {
131135
const SizedBox(width: 8),
132136
Expanded(child: Padding(
133137
padding: const EdgeInsets.symmetric(vertical: 4),
134-
child: Text(
135-
style: const TextStyle(
136-
fontSize: 17,
137-
height: (20 / 17),
138-
// TODO(#95) need dark-theme color
139-
color: Color(0xFF222222),
140-
),
141-
maxLines: 2,
142-
overflow: TextOverflow.ellipsis,
143-
title))),
138+
child: Row(
139+
children: [
140+
Flexible(child: Text(
141+
style: const TextStyle(
142+
fontSize: 17,
143+
height: (20 / 17),
144+
// TODO(#95) need dark-theme color
145+
color: Color(0xFF222222),
146+
),
147+
maxLines: 2,
148+
overflow: TextOverflow.ellipsis,
149+
title)),
150+
if (isBot) ...[
151+
const SizedBox(width: 5),
152+
const Icon(
153+
ZulipIcons.bot,
154+
size: 15,
155+
color: Color.fromARGB(255, 159, 173, 173),
156+
),
157+
],
158+
]))),
144159
const SizedBox(width: 12),
145160
unreadCount > 0
146161
? Padding(padding: const EdgeInsetsDirectional.only(end: 16),

test/widgets/recent_dm_conversations_test.dart

Lines changed: 101 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,14 @@ void main() {
149149
}
150150
}
151151

152+
void checkBotIcon({required bool appears}) {
153+
final botFinder = find.descendant(
154+
of: find.byType(RecentDmConversationsItem),
155+
matching: find.byIcon(ZulipIcons.bot));
156+
157+
check(botFinder.evaluate().length).equals(appears ? 1 : 0);
158+
}
159+
152160
Future<void> markMessageAsRead(WidgetTester tester, Message message) async {
153161
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
154162
await store.handleEvent(UpdateMessageFlagsAddEvent(
@@ -176,6 +184,7 @@ void main() {
176184

177185
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
178186
checkTitle(tester, eg.selfUser.fullName);
187+
checkBotIcon(appears: false);
179188
});
180189

181190
testWidgets('short name takes one line', (WidgetTester tester) async {
@@ -184,6 +193,7 @@ void main() {
184193
await setupPage(tester, users: [], dmMessages: [message],
185194
newNameForSelfUser: name);
186195
checkTitle(tester, name, 1);
196+
checkBotIcon(appears: false);
187197
});
188198

189199
testWidgets('very long name takes two lines (must be ellipsized)', (WidgetTester tester) async {
@@ -192,6 +202,7 @@ void main() {
192202
await setupPage(tester, users: [], dmMessages: [message],
193203
newNameForSelfUser: name);
194204
checkTitle(tester, name, 2);
205+
checkBotIcon(appears: false);
195206
});
196207

197208
testWidgets('unread counts', (WidgetTester tester) async {
@@ -201,43 +212,95 @@ void main() {
201212
checkUnreadCount(tester, 1);
202213
await markMessageAsRead(tester, message);
203214
checkUnreadCount(tester, 0);
215+
checkBotIcon(appears: false);
204216
});
205217
});
206218

207219
group('1:1', () {
208-
testWidgets('has right title/avatar', (WidgetTester tester) async {
209-
final user = eg.user(userId: 1);
210-
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
211-
await setupPage(tester, users: [user], dmMessages: [message]);
212-
213-
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
214-
checkTitle(tester, user.fullName);
220+
group('has right title/avatar', () {
221+
testWidgets('bot recipient -> shows bot icon', (tester) async {
222+
final user = eg.user(userId: 1, isBot: true);
223+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
224+
await setupPage(tester, users: [user], dmMessages: [message]);
225+
226+
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
227+
checkTitle(tester, user.fullName);
228+
checkBotIcon(appears: true);
229+
});
230+
231+
testWidgets('non-bot recipient -> shows no bot icon', (tester) async {
232+
final user = eg.user(userId: 1, isBot: false);
233+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
234+
await setupPage(tester, users: [user], dmMessages: [message]);
235+
236+
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
237+
checkTitle(tester, user.fullName);
238+
checkBotIcon(appears: false);
239+
});
215240
});
216241

217-
testWidgets('no error when user somehow missing from store.users', (WidgetTester tester) async {
218-
final user = eg.user(userId: 1);
219-
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
220-
await setupPage(tester,
221-
users: [], // exclude user
222-
dmMessages: [message],
223-
);
224-
225-
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
226-
checkTitle(tester, '(unknown user)');
242+
group('no error when user somehow missing from store.users', () {
243+
testWidgets('bot recipient -> shows no bot icon', (tester) async {
244+
final user = eg.user(userId: 1, isBot: true);
245+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
246+
await setupPage(tester,
247+
users: [], // exclude user
248+
dmMessages: [message],
249+
);
250+
251+
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
252+
checkTitle(tester, '(unknown user)');
253+
checkBotIcon(appears: false);
254+
});
255+
256+
testWidgets('non-bot recipient -> shows no bot icon', (tester) async {
257+
final user = eg.user(userId: 1, isBot: false);
258+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
259+
await setupPage(tester,
260+
users: [], // exclude user
261+
dmMessages: [message],
262+
);
263+
264+
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
265+
checkTitle(tester, '(unknown user)');
266+
checkBotIcon(appears: false);
267+
});
227268
});
228269

229-
testWidgets('short name takes one line', (WidgetTester tester) async {
230-
final user = eg.user(userId: 1, fullName: 'Short name');
231-
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
232-
await setupPage(tester, users: [user], dmMessages: [message]);
233-
checkTitle(tester, user.fullName, 1);
270+
group('short name takes one line', () {
271+
testWidgets('bot recipient -> shows bot icon', (tester) async {
272+
final user = eg.user(userId: 1, fullName: 'Short name', isBot: true);
273+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
274+
await setupPage(tester, users: [user], dmMessages: [message]);
275+
checkTitle(tester, user.fullName, 1);
276+
checkBotIcon(appears: true);
277+
});
278+
279+
testWidgets('non-bot recipient -> shows no bot icon', (tester) async {
280+
final user = eg.user(userId: 1, fullName: 'Short name', isBot: false);
281+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
282+
await setupPage(tester, users: [user], dmMessages: [message]);
283+
checkTitle(tester, user.fullName, 1);
284+
checkBotIcon(appears: false);
285+
});
234286
});
235287

236-
testWidgets('very long name takes two lines (must be ellipsized)', (WidgetTester tester) async {
237-
final user = eg.user(userId: 1, fullName: 'Long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name');
238-
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
239-
await setupPage(tester, users: [user], dmMessages: [message]);
240-
checkTitle(tester, user.fullName, 2);
288+
group('very long name takes two lines (must be ellipsized)', () {
289+
testWidgets('bot recipient -> shows bot icon', (WidgetTester tester) async {
290+
final user = eg.user(userId: 1, isBot: true, fullName: 'Long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name');
291+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
292+
await setupPage(tester, users: [user], dmMessages: [message]);
293+
checkTitle(tester, user.fullName, 2);
294+
checkBotIcon(appears: true);
295+
});
296+
297+
testWidgets('non-bot recipient -> shows no bot icon', (WidgetTester tester) async {
298+
final user = eg.user(userId: 1, isBot: false, fullName: 'Long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name long name');
299+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
300+
await setupPage(tester, users: [user], dmMessages: [message]);
301+
checkTitle(tester, user.fullName, 2);
302+
checkBotIcon(appears: false);
303+
});
241304
});
242305

243306
testWidgets('unread counts', (WidgetTester tester) async {
@@ -251,27 +314,29 @@ void main() {
251314
});
252315

253316
group('group', () {
254-
List<User> usersList(int count) {
317+
List<User> usersList(int count, {bool? containsBot}) {
255318
final result = <User>[];
256319
for (int i = 0; i < count; i++) {
257-
result.add(eg.user(userId: i, fullName: 'User ${i.toString()}'));
320+
result.add(eg.user(userId: i, fullName: 'User ${i.toString()}',
321+
isBot: (containsBot ?? false) && i == 0));
258322
}
259323
return result;
260324
}
261325

262326
testWidgets('has right title/avatar', (WidgetTester tester) async {
263-
final users = usersList(2);
327+
final users = usersList(2, containsBot: true);
264328
final user0 = users[0];
265329
final user1 = users[1];
266330
final message = eg.dmMessage(from: eg.selfUser, to: [user0, user1]);
267331
await setupPage(tester, users: users, dmMessages: [message]);
268332

269333
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
270334
checkTitle(tester, '${user0.fullName}, ${user1.fullName}');
335+
checkBotIcon(appears: false);
271336
});
272337

273338
testWidgets('no error when one user somehow missing from store.users', (WidgetTester tester) async {
274-
final users = usersList(2);
339+
final users = usersList(2, containsBot: false);
275340
final user0 = users[0];
276341
final user1 = users[1];
277342
final message = eg.dmMessage(from: eg.selfUser, to: [user0, user1]);
@@ -282,20 +347,23 @@ void main() {
282347

283348
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
284349
checkTitle(tester, '${user0.fullName}, (unknown user)');
350+
checkBotIcon(appears: false);
285351
});
286352

287353
testWidgets('few names takes one line', (WidgetTester tester) async {
288-
final users = usersList(2);
354+
final users = usersList(2, containsBot: false);
289355
final message = eg.dmMessage(from: eg.selfUser, to: users);
290356
await setupPage(tester, users: users, dmMessages: [message]);
291357
checkTitle(tester, users.map((u) => u.fullName).join(', '), 1);
358+
checkBotIcon(appears: false);
292359
});
293360

294361
testWidgets('very many names takes two lines (must be ellipsized)', (WidgetTester tester) async {
295-
final users = usersList(40);
362+
final users = usersList(40, containsBot: true);
296363
final message = eg.dmMessage(from: eg.selfUser, to: users);
297364
await setupPage(tester, users: users, dmMessages: [message]);
298365
checkTitle(tester, users.map((u) => u.fullName).join(', '), 2);
366+
checkBotIcon(appears: false);
299367
});
300368

301369
testWidgets('unread counts', (WidgetTester tester) async {

0 commit comments

Comments
 (0)