Skip to content

Commit b6d8f03

Browse files
committed
narrow: Add starred messages narrow.
"Starred messages narrow" is the user facing name of the narrow, thus we name the Narrow class after it. This narrow is pretty similar to MentionsNarrow. Signed-off-by: Zixuan James Li <[email protected]>
1 parent efad2ad commit b6d8f03

17 files changed

+137
-8
lines changed

assets/l10n/app_en.arb

+4
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,10 @@
480480
"@mentionsPageTitle": {
481481
"description": "Title for the page of @-mentions."
482482
},
483+
"starredMessagesPageTitle": "Starred messages",
484+
"@starredMessagesPageTitle": {
485+
"description": "Title for the page of starred messages."
486+
},
483487
"notifGroupDmConversationLabel": "{senderFullName} to you and {numOthers, plural, =1{1 other} other{{numOthers} others}}",
484488
"@notifGroupDmConversationLabel": {
485489
"description": "Label for a group DM conversation notification.",

lib/model/autocomplete.dart

+1
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ class MentionAutocompleteView extends ChangeNotifier {
223223
break;
224224
case CombinedFeedNarrow():
225225
case MentionsNarrow():
226+
case StarredMessagesNarrow():
226227
assert(false, 'No compose box, thus no autocomplete is available in ${narrow.runtimeType}.');
227228
}
228229
return (userA, userB) => _compareByRelevance(userA, userB,

lib/model/message_list.dart

+2
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ class MessageListView with ChangeNotifier, _MessageSequence {
371371
case TopicNarrow():
372372
case DmNarrow():
373373
case MentionsNarrow():
374+
case StarredMessagesNarrow():
374375
return true;
375376
}
376377
}
@@ -387,6 +388,7 @@ class MessageListView with ChangeNotifier, _MessageSequence {
387388
case TopicNarrow():
388389
case DmNarrow():
389390
case MentionsNarrow():
391+
case StarredMessagesNarrow():
390392
return true;
391393
}
392394
}

lib/model/narrow.dart

+22
Original file line numberDiff line numberDiff line change
@@ -322,3 +322,25 @@ class MentionsNarrow extends Narrow {
322322
@override
323323
int get hashCode => 'MentionsNarrow'.hashCode;
324324
}
325+
326+
class StarredMessagesNarrow extends Narrow {
327+
const StarredMessagesNarrow();
328+
329+
@override
330+
ApiNarrow apiEncode() => [ApiNarrowIs(IsOperand.starred)];
331+
332+
@override
333+
bool containsMessage(Message message) {
334+
return message.flags.contains(MessageFlag.starred);
335+
}
336+
337+
@override
338+
bool operator ==(Object other) {
339+
if (other is! StarredMessagesNarrow) return false;
340+
// Conceptually there's only one value of this type.
341+
return true;
342+
}
343+
344+
@override
345+
int get hashCode => 'StarredMessagesNarrow'.hashCode;
346+
}

lib/model/unreads.dart

+5
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ class Unreads extends ChangeNotifier {
195195

196196
int countInMentionsNarrow() => mentions.length;
197197

198+
// TODO: Implement unreads handling.
199+
int countInStarredMessagesNarrow() => 0;
200+
198201
int countInNarrow(Narrow narrow) {
199202
switch (narrow) {
200203
case CombinedFeedNarrow():
@@ -207,6 +210,8 @@ class Unreads extends ChangeNotifier {
207210
return countInDmNarrow(narrow);
208211
case MentionsNarrow():
209212
return countInMentionsNarrow();
213+
case StarredMessagesNarrow():
214+
return countInStarredMessagesNarrow();
210215
}
211216
}
212217

lib/widgets/actions.dart

+3
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,8 @@ Future<void> _legacyMarkNarrowAsRead(BuildContext context, Narrow narrow) async
142142
messages: unreadMentions,
143143
op: UpdateMessageFlagsOp.add,
144144
flag: MessageFlag.read);
145+
case StarredMessagesNarrow():
146+
// TODO: Implement unreads handling.
147+
return;
145148
}
146149
}

lib/widgets/compose_box.dart

+2
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,7 @@ class ComposeBox extends StatelessWidget {
10721072

10731073
case CombinedFeedNarrow():
10741074
case MentionsNarrow():
1075+
case StarredMessagesNarrow():
10751076
return false;
10761077
}
10771078
}
@@ -1088,6 +1089,7 @@ class ComposeBox extends StatelessWidget {
10881089
return _FixedDestinationComposeBox(key: controllerKey, narrow: narrow);
10891090
case CombinedFeedNarrow():
10901091
case MentionsNarrow():
1092+
case StarredMessagesNarrow():
10911093
return const SizedBox.shrink();
10921094
}
10931095
}

lib/widgets/message_list.dart

+5
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ class _MessageListPageState extends State<MessageListPage> implements MessageLis
236236
switch(widget.narrow) {
237237
case CombinedFeedNarrow():
238238
case MentionsNarrow():
239+
case StarredMessagesNarrow():
239240
appBarBackgroundColor = null; // i.e., inherit
240241

241242
case ChannelNarrow(:final streamId):
@@ -324,6 +325,9 @@ class MessageListAppBarTitle extends StatelessWidget {
324325
case MentionsNarrow():
325326
return Text(zulipLocalizations.mentionsPageTitle);
326327

328+
case StarredMessagesNarrow():
329+
return Text(zulipLocalizations.starredMessagesPageTitle);
330+
327331
case ChannelNarrow(:var streamId):
328332
final store = PerAccountStoreWidget.of(context);
329333
final stream = store.streams[streamId];
@@ -815,6 +819,7 @@ class RecipientHeader extends StatelessWidget {
815819
switch (narrow) {
816820
case CombinedFeedNarrow():
817821
case MentionsNarrow():
822+
case StarredMessagesNarrow():
818823
return true;
819824

820825
case ChannelNarrow():

test/api/route/messages_test.dart

+3
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ void main() {
191191
checkNarrow(const MentionsNarrow().apiEncode(), jsonEncode([
192192
{'operator': 'is', 'operand': 'mentioned'},
193193
]));
194+
checkNarrow(const StarredMessagesNarrow().apiEncode(), jsonEncode([
195+
{'operator': 'is', 'operand': 'starred'},
196+
]));
194197

195198
checkNarrow([ApiNarrowDm([123, 234])], jsonEncode([
196199
{'operator': 'dm', 'operand': [123, 234]},

test/model/autocomplete_test.dart

+7
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,13 @@ void main() {
578578
check(() => MentionAutocompleteView.init(store: store, narrow: narrow))
579579
.throws<AssertionError>();
580580
});
581+
582+
test('StarredMessagesNarrow gives error', () async {
583+
await prepare(users: [eg.user(), eg.user()], messages: []);
584+
const narrow = StarredMessagesNarrow();
585+
check(() => MentionAutocompleteView.init(store: store, narrow: narrow))
586+
.throws<AssertionError>();
587+
});
581588
});
582589

583590
test('final results end-to-end', () async {

test/model/compose_test.dart

+8
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,14 @@ hello
239239
.equals(store.realmUrl.resolve('#narrow/is/mentioned/near/1'));
240240
});
241241

242+
test('StarredMessagesNarrow', () {
243+
final store = eg.store();
244+
check(narrowLink(store, const StarredMessagesNarrow()))
245+
.equals(store.realmUrl.resolve('#narrow/is/starred'));
246+
check(narrowLink(store, const StarredMessagesNarrow(), nearMessageId: 1))
247+
.equals(store.realmUrl.resolve('#narrow/is/starred/near/1'));
248+
});
249+
242250
test('ChannelNarrow / TopicNarrow', () {
243251
void checkNarrow(String expectedFragment, {
244252
required int streamId,

test/model/internal_link_test.dart

+1
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ void main() {
245245
case IsOperand.private:
246246
case IsOperand.alerted:
247247
case IsOperand.starred:
248+
testCases = sharedCases(const StarredMessagesNarrow());
248249
case IsOperand.followed:
249250
case IsOperand.resolved:
250251
case IsOperand.unread:

test/model/message_list_test.dart

+39
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,44 @@ void main() {
774774
check(model.messages.map((m) => m.id)).deepEquals(expected..add(301 + i));
775775
}
776776
});
777+
778+
test('in StarredMessagesNarrow', () async {
779+
final stream = eg.stream(streamId: 1, name: 'muted stream');
780+
const mutedTopic = 'muted';
781+
await prepare(narrow: const StarredMessagesNarrow());
782+
await store.addStream(stream);
783+
await store.addUserTopic(stream, mutedTopic, UserTopicVisibilityPolicy.muted);
784+
await store.addSubscription(eg.subscription(stream, isMuted: true));
785+
786+
List<Message> getMessages(int startingId) => [
787+
eg.streamMessage(id: startingId,
788+
stream: stream, topic: mutedTopic, flags: [MessageFlag.starred]),
789+
eg.dmMessage(id: startingId + 1,
790+
from: eg.otherUser, to: [eg.selfUser], flags: [MessageFlag.starred]),
791+
];
792+
793+
// Check filtering on fetchInitial…
794+
await prepareMessages(foundOldest: false, messages: getMessages(201));
795+
final expected = <int>[];
796+
check(model.messages.map((m) => m.id))
797+
.deepEquals(expected..addAll([201, 202]));
798+
799+
// … and on fetchOlder…
800+
connection.prepare(json: olderResult(
801+
anchor: 201, foundOldest: true, messages: getMessages(101)).toJson());
802+
await model.fetchOlder();
803+
checkNotified(count: 2);
804+
check(model.messages.map((m) => m.id))
805+
.deepEquals(expected..insertAll(0, [101, 102]));
806+
807+
// … and on MessageEvent.
808+
final messages = getMessages(301);
809+
for (var i = 0; i < 2; i += 1) {
810+
await store.handleEvent(MessageEvent(id: 0, message: messages[i]));
811+
checkNotifiedOnce();
812+
check(model.messages.map((m) => m.id)).deepEquals(expected..add(301 + i));
813+
}
814+
});
777815
});
778816

779817
test('recipient headers are maintained consistently', () async {
@@ -1020,6 +1058,7 @@ void checkInvariants(MessageListView model) {
10201058
case TopicNarrow():
10211059
case DmNarrow():
10221060
case MentionsNarrow():
1061+
case StarredMessagesNarrow():
10231062
}
10241063
}
10251064

test/model/narrow_test.dart

+11
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,15 @@ void main() {
162162
eg.streamMessage(flags: [MessageFlag.wildcardMentioned]))).isTrue();
163163
});
164164
});
165+
166+
group('StarredMessagesNarrow', () {
167+
test('containsMessage', () {
168+
const narrow = StarredMessagesNarrow();
169+
170+
check(narrow.containsMessage(
171+
eg.streamMessage(flags: []))).isFalse();
172+
check(narrow.containsMessage(
173+
eg.streamMessage(flags:[MessageFlag.starred]))).isTrue();
174+
});
175+
});
165176
}

test/model/unreads_test.dart

+11
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,17 @@ void main() {
227227
]);
228228
check(model.countInMentionsNarrow()).equals(2);
229229
});
230+
231+
test('countInStarredMessagesNarrow', () async {
232+
final stream = eg.stream();
233+
prepare();
234+
await channelStore.addStream(stream);
235+
fillWithMessages([
236+
eg.streamMessage(stream: stream, flags: []),
237+
eg.streamMessage(stream: stream, flags: [MessageFlag.starred]),
238+
]);
239+
check(model.countInStarredMessagesNarrow()).equals(0);
240+
});
230241
});
231242

232243
group('handleMessageEvent', () {

test/widgets/action_sheet_test.dart

+3-2
Original file line numberDiff line numberDiff line change
@@ -374,8 +374,9 @@ void main() {
374374

375375
group('composing to reply is not yet supported', () {
376376
final testCases = [
377-
('CombinedFeedNarrow', const CombinedFeedNarrow(), eg.streamMessage()),
378-
('MentionsNarrow', const MentionsNarrow(), eg.streamMessage(flags: [MessageFlag.mentioned])),
377+
('CombinedFeedNarrow', const CombinedFeedNarrow(), eg.streamMessage()),
378+
('MentionsNarrow', const MentionsNarrow(), eg.streamMessage(flags: [MessageFlag.mentioned])),
379+
('StarredMessagesNarrow', const StarredMessagesNarrow(), eg.streamMessage(flags: [MessageFlag.starred])),
379380
];
380381

381382
for (final (narrowName, narrow, message) in testCases) {

test/widgets/message_list_test.dart

+10-6
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,9 @@ void main() {
125125
group('presents message content appropriately', () {
126126
// regression test for https://github.com/zulip/zulip-flutter/issues/736
127127
final testCases = [
128-
("Combined feed", const CombinedFeedNarrow(), <MessageFlag>[]),
129-
("MentionsNarrow", const MentionsNarrow(), [MessageFlag.mentioned]),
128+
("Combined feed", const CombinedFeedNarrow(), <MessageFlag>[]),
129+
("MentionsNarrow", const MentionsNarrow(), [MessageFlag.mentioned]),
130+
("StarredMessagesNarrow", const StarredMessagesNarrow(), [MessageFlag.starred]),
130131
];
131132
for (final (narrowName, narrow, flags) in testCases) {
132133
testWidgets('content in $narrowName not asked to consume insets (including bottom)', (tester) async {
@@ -629,11 +630,14 @@ void main() {
629630
group('show channel name conditionally', () {
630631
final mentionedMessage =
631632
eg.streamMessage(stream: stream, topic: 'topic name', flags: [MessageFlag.mentioned]);
633+
final starredMessage =
634+
eg.streamMessage(stream: stream, topic: 'topic name', flags: [MessageFlag.starred]);
632635
final testCases = [
633-
(true, 'CombinedFeedNarrow', const CombinedFeedNarrow(), message),
634-
(true, 'MentionsNarrow', const MentionsNarrow(), mentionedMessage),
635-
(false, 'ChannelNarrow', ChannelNarrow(stream.streamId), message),
636-
(false, 'TopicNarrow', TopicNarrow.ofMessage(message), message)
636+
(true, 'CombinedFeedNarrow', const CombinedFeedNarrow(), message),
637+
(true, 'MentionsNarrow', const MentionsNarrow(), mentionedMessage),
638+
(true, 'StarredMessagesNarrow', const StarredMessagesNarrow(), starredMessage),
639+
(false, 'ChannelNarrow', ChannelNarrow(stream.streamId), message),
640+
(false, 'TopicNarrow', TopicNarrow.ofMessage(message), message)
637641
];
638642

639643
for (final (showChannelName, narrowName, narrow, message) in testCases) {

0 commit comments

Comments
 (0)