Skip to content

Commit fbbf986

Browse files
committed
recent_senders: Treat topics case-insensitively
1 parent 0972bee commit fbbf986

File tree

3 files changed

+45
-5
lines changed

3 files changed

+45
-5
lines changed

lib/model/recent_senders.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
import 'dart:collection';
2+
13
import 'package:collection/collection.dart';
24
import 'package:flutter/foundation.dart';
35

46
import '../api/model/events.dart';
57
import '../api/model/model.dart';
68
import 'algorithms.dart';
9+
import 'channel.dart';
710

811
/// Tracks the latest messages sent by each user, in each stream and topic.
912
///
@@ -16,7 +19,7 @@ class RecentSenders {
1619

1720
// topicSenders[streamId][topic][senderId] = MessageIdTracker
1821
@visibleForTesting
19-
final Map<int, Map<TopicName, Map<int, MessageIdTracker>>> topicSenders = {};
22+
final Map<int, LinkedHashMap<TopicName, Map<int, MessageIdTracker>>> topicSenders = {};
2023

2124
/// The latest message the given user sent to the given stream,
2225
/// or null if no such message is known.
@@ -53,7 +56,7 @@ class RecentSenders {
5356
}
5457
for (final entry in messagesByUserInTopic.entries) {
5558
final (streamId, topic, senderId) = entry.key;
56-
(((topicSenders[streamId] ??= {})[topic] ??= {})
59+
(((topicSenders[streamId] ??= makeTopicKeyedMap())[topic] ??= {})
5760
[senderId] ??= MessageIdTracker()).addAll(entry.value);
5861
}
5962
}
@@ -64,7 +67,7 @@ class RecentSenders {
6467
final StreamMessage(:streamId, :topic, :senderId, id: int messageId) = message;
6568
((streamSenders[streamId] ??= {})
6669
[senderId] ??= MessageIdTracker()).add(messageId);
67-
(((topicSenders[streamId] ??= {})[topic] ??= {})
70+
(((topicSenders[streamId] ??= makeTopicKeyedMap())[topic] ??= {})
6871
[senderId] ??= MessageIdTracker()).add(messageId);
6972
}
7073

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import 'package:checks/checks.dart';
2+
import 'package:collection/collection.dart';
3+
import 'package:zulip/model/recent_senders.dart';
4+
5+
extension MessageIdTrackerChecks on Subject<MessageIdTracker> {
6+
Subject<QueueList<int>> get ids => has((x) => x.ids, 'ids');
7+
}

test/model/recent_senders_test.dart

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
1+
import 'dart:collection';
2+
13
import 'package:checks/checks.dart';
24
import 'package:flutter_test/flutter_test.dart';
5+
import 'package:zulip/api/model/events.dart';
36
import 'package:zulip/api/model/model.dart';
7+
import 'package:zulip/model/channel.dart';
48
import 'package:zulip/model/recent_senders.dart';
59
import '../example_data.dart' as eg;
10+
import '../stdlib_checks.dart';
11+
import 'recent_senders_checks.dart';
612

713
/// [messages] should be sorted by [id] ascending.
814
void checkMatchesMessages(RecentSenders model, List<Message> messages) {
915
final Map<int, Map<int, Set<int>>> messagesByUserInStream = {};
10-
final Map<int, Map<TopicName, Map<int, Set<int>>>> messagesByUserInTopic = {};
16+
final Map<int, LinkedHashMap<TopicName, Map<int, Set<int>>>> messagesByUserInTopic = {};
1117
for (final message in messages) {
1218
if (message is! StreamMessage) {
1319
throw UnsupportedError('Message of type ${message.runtimeType} is not expected.');
@@ -17,7 +23,7 @@ void checkMatchesMessages(RecentSenders model, List<Message> messages) {
1723

1824
((messagesByUserInStream[streamId] ??= {})
1925
[senderId] ??= {}).add(messageId);
20-
(((messagesByUserInTopic[streamId] ??= {})[topic] ??= {})
26+
(((messagesByUserInTopic[streamId] ??= makeTopicKeyedMap())[topic] ??= {})
2127
[senderId] ??= {}).add(messageId);
2228
}
2329

@@ -125,6 +131,17 @@ void main() {
125131
[eg.streamMessage(stream: streamA, topic: 'other', sender: userX)]);
126132
});
127133

134+
test('case-insensitive topics', () {
135+
checkHandleMessages(
136+
[eg.streamMessage(stream: streamA, topic: 'thing', sender: userX)],
137+
[eg.streamMessage(stream: streamA, topic: 'ThInG', sender: userX)]);
138+
check(model.topicSenders).values.single.entries.single
139+
..key.equals(eg.t('thing'))
140+
..value.entries.single.which((it) => it
141+
..key.equals(userX.userId)
142+
..value.isA<MessageIdTracker>().ids.length.equals(2));
143+
});
144+
128145
test('add new stream', () {
129146
checkHandleMessages(
130147
[eg.streamMessage(stream: streamA, topic: 'thing', sender: userX)],
@@ -161,6 +178,16 @@ void main() {
161178
Map.fromEntries(messages.map((msg) => MapEntry(msg.id, msg))));
162179

163180
checkMatchesMessages(model, [messages[1]]);
181+
182+
// check case-insensitivity
183+
model.handleDeleteMessageEvent(DeleteMessageEvent(
184+
id: 0,
185+
messageIds: [messages[1].id],
186+
messageType: MessageType.stream,
187+
streamId: stream.streamId,
188+
topic: eg.t('oThEr'),
189+
), {messages[1].id: messages[1]});
190+
checkMatchesMessages(model, []);
164191
});
165192

166193
test('RecentSenders.latestMessageIdOfSenderInStream', () {
@@ -200,6 +227,9 @@ void main() {
200227

201228
check(model.latestMessageIdOfSenderInTopic(streamId: 1,
202229
topic: eg.t('a'), senderId: 10)).equals(300);
230+
// case-insensitivity
231+
check(model.latestMessageIdOfSenderInTopic(streamId: 1,
232+
topic: eg.t('A'), senderId: 10)).equals(300);
203233
// No message of user 20 in topic "a".
204234
check(model.latestMessageIdOfSenderInTopic(streamId: 1,
205235
topic: eg.t('a'), senderId: 20)).equals(null);

0 commit comments

Comments
 (0)