Skip to content

Commit 7e72578

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

File tree

2 files changed

+129
-41
lines changed

2 files changed

+129
-41
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: 104 additions & 31 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,100 @@ 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]);
220+
group('has right title/avatar', () {
221+
Future<void> checkRecipient(WidgetTester tester, {
222+
required bool isBot,
223+
required bool looksBot
224+
}) async {
225+
final user = eg.user(userId: 1, isBot: isBot);
226+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
227+
await setupPage(tester, users: [user], dmMessages: [message]);
228+
229+
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
230+
checkTitle(tester, user.fullName);
231+
checkBotIcon(appears: looksBot);
232+
}
212233

213-
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
214-
checkTitle(tester, user.fullName);
234+
testWidgets('bot recipient -> shows bot icon', (tester) async {
235+
await checkRecipient(tester, isBot: true, looksBot: true);
236+
});
237+
238+
testWidgets('non-bot recipient -> shows no bot icon', (tester) async {
239+
await checkRecipient(tester, isBot: false, looksBot: false);
240+
});
215241
});
216242

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-
);
243+
group('no error when user somehow missing from store.users', () {
244+
Future<void> checkRecipient(WidgetTester tester, {
245+
required bool isBot,
246+
required bool looksBot
247+
}) async {
248+
final user = eg.user(userId: 1, isBot: isBot);
249+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
250+
await setupPage(tester,
251+
users: [], // exclude user
252+
dmMessages: [message],
253+
);
254+
255+
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
256+
checkTitle(tester, '(unknown user)');
257+
checkBotIcon(appears: looksBot);
258+
}
224259

225-
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
226-
checkTitle(tester, '(unknown user)');
260+
testWidgets('bot recipient -> shows no bot icon', (tester) async {
261+
await checkRecipient(tester, isBot: true, looksBot: false);
262+
});
263+
264+
testWidgets('non-bot recipient -> shows no bot icon', (tester) async {
265+
await checkRecipient(tester, isBot: false, looksBot: false);
266+
});
227267
});
228268

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);
269+
group('short name takes one line', () {
270+
Future<void> checkRecipient(WidgetTester tester, {
271+
required bool isBot,
272+
required bool looksBot
273+
}) async {
274+
final user = eg.user(userId: 1, fullName: 'Short name', isBot: isBot);
275+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
276+
await setupPage(tester, users: [user], dmMessages: [message]);
277+
checkTitle(tester, user.fullName, 1);
278+
checkBotIcon(appears: looksBot);
279+
}
280+
281+
testWidgets('bot recipient -> shows bot icon', (tester) async {
282+
await checkRecipient(tester, isBot: true, looksBot: true);
283+
});
284+
285+
testWidgets('non-bot recipient -> shows no bot icon', (tester) async {
286+
await checkRecipient(tester, isBot: false, looksBot: false);
287+
});
234288
});
235289

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);
290+
group('very long name takes two lines (must be ellipsized)', () {
291+
Future<void> checkRecipient(WidgetTester tester, {
292+
required bool isBot,
293+
required bool looksBot
294+
}) async {
295+
final user = eg.user(userId: 1, isBot: isBot, 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');
296+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
297+
await setupPage(tester, users: [user], dmMessages: [message]);
298+
checkTitle(tester, user.fullName, 2);
299+
checkBotIcon(appears: looksBot);
300+
}
301+
302+
testWidgets('bot recipient -> shows bot icon', (WidgetTester tester) async {
303+
await checkRecipient(tester, isBot: true, looksBot: true);
304+
});
305+
306+
testWidgets('non-bot recipient -> shows no bot icon', (WidgetTester tester) async {
307+
await checkRecipient(tester, isBot: false, looksBot: false);
308+
});
241309
});
242310

243311
testWidgets('unread counts', (WidgetTester tester) async {
@@ -251,27 +319,29 @@ void main() {
251319
});
252320

253321
group('group', () {
254-
List<User> usersList(int count) {
322+
List<User> usersList(int count, {bool? containsBot}) {
255323
final result = <User>[];
256324
for (int i = 0; i < count; i++) {
257-
result.add(eg.user(userId: i, fullName: 'User ${i.toString()}'));
325+
result.add(eg.user(userId: i, fullName: 'User ${i.toString()}',
326+
isBot: (containsBot ?? false) && i == 0));
258327
}
259328
return result;
260329
}
261330

262331
testWidgets('has right title/avatar', (WidgetTester tester) async {
263-
final users = usersList(2);
332+
final users = usersList(2, containsBot: true);
264333
final user0 = users[0];
265334
final user1 = users[1];
266335
final message = eg.dmMessage(from: eg.selfUser, to: [user0, user1]);
267336
await setupPage(tester, users: users, dmMessages: [message]);
268337

269338
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
270339
checkTitle(tester, '${user0.fullName}, ${user1.fullName}');
340+
checkBotIcon(appears: false);
271341
});
272342

273343
testWidgets('no error when one user somehow missing from store.users', (WidgetTester tester) async {
274-
final users = usersList(2);
344+
final users = usersList(2, containsBot: false);
275345
final user0 = users[0];
276346
final user1 = users[1];
277347
final message = eg.dmMessage(from: eg.selfUser, to: [user0, user1]);
@@ -282,20 +352,23 @@ void main() {
282352

283353
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
284354
checkTitle(tester, '${user0.fullName}, (unknown user)');
355+
checkBotIcon(appears: false);
285356
});
286357

287358
testWidgets('few names takes one line', (WidgetTester tester) async {
288-
final users = usersList(2);
359+
final users = usersList(2, containsBot: false);
289360
final message = eg.dmMessage(from: eg.selfUser, to: users);
290361
await setupPage(tester, users: users, dmMessages: [message]);
291362
checkTitle(tester, users.map((u) => u.fullName).join(', '), 1);
363+
checkBotIcon(appears: false);
292364
});
293365

294366
testWidgets('very many names takes two lines (must be ellipsized)', (WidgetTester tester) async {
295-
final users = usersList(40);
367+
final users = usersList(40, containsBot: true);
296368
final message = eg.dmMessage(from: eg.selfUser, to: users);
297369
await setupPage(tester, users: users, dmMessages: [message]);
298370
checkTitle(tester, users.map((u) => u.fullName).join(', '), 2);
371+
checkBotIcon(appears: false);
299372
});
300373

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

0 commit comments

Comments
 (0)