Skip to content

Commit eed56a2

Browse files
committed
message: Add MessageStore.starredMessages
1 parent d1fef86 commit eed56a2

File tree

4 files changed

+104
-11
lines changed

4 files changed

+104
-11
lines changed

lib/model/message.dart

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ mixin MessageStore on ChannelStore {
2424
/// All known messages, indexed by [Message.id].
2525
Map<int, Message> get messages;
2626

27+
/// All starred messages, as message IDs.
28+
Set<int> get starredMessages;
29+
2730
/// [OutboxMessage]s sent by the user, indexed by [OutboxMessage.localMessageId].
2831
Map<int, OutboxMessage> get outboxMessages;
2932

@@ -208,6 +211,8 @@ mixin ProxyMessageStore on MessageStore {
208211
@override
209212
Map<int, Message> get messages => messageStore.messages;
210213
@override
214+
Set<int> get starredMessages => messageStore.starredMessages;
215+
@override
211216
Map<int, OutboxMessage> get outboxMessages => messageStore.outboxMessages;
212217
@override
213218
void registerMessageList(MessageListView view) =>
@@ -261,14 +266,19 @@ class _EditMessageRequestStatus {
261266
}
262267

263268
class MessageStoreImpl extends HasChannelStore with MessageStore, _OutboxMessageStore {
264-
MessageStoreImpl({required super.channels})
265-
: // There are no messages in InitialSnapshot, so we don't have
266-
// a use case for initializing MessageStore with nonempty [messages].
267-
messages = {};
269+
MessageStoreImpl({
270+
required super.channels,
271+
required List<int> initialStarredMessages,
272+
}) :
273+
messages = {},
274+
starredMessages = Set.of(initialStarredMessages);
268275

269276
@override
270277
final Map<int, Message> messages;
271278

279+
@override
280+
final Set<int> starredMessages;
281+
272282
@override
273283
final Set<MessageListView> _messageListViews = {};
274284

@@ -717,23 +727,29 @@ class MessageStoreImpl extends HasChannelStore with MessageStore, _OutboxMessage
717727
}
718728
}
719729

720-
void handleDeleteMessageEvent(DeleteMessageEvent event) {
730+
/// Handle a [DeleteMessageEvent]
731+
/// and return whether the [PerAccountStore] should notify listeners.
732+
bool handleDeleteMessageEvent(DeleteMessageEvent event) {
733+
bool perAccountStoreShouldNotify = false;
721734
for (final messageId in event.messageIds) {
722735
messages.remove(messageId);
736+
perAccountStoreShouldNotify |= starredMessages.remove(messageId);
723737
_maybeStaleChannelMessages.remove(messageId);
724738
_editMessageRequests.remove(messageId);
725739
}
726740
for (final view in _messageListViews) {
727741
view.handleDeleteMessageEvent(event);
728742
}
743+
return perAccountStoreShouldNotify;
729744
}
730745

731-
void handleUpdateMessageFlagsEvent(UpdateMessageFlagsEvent event) {
746+
/// Handle an [UpdateMessageFlagsEvent]
747+
/// and return whether the [PerAccountStore] should notify listeners.
748+
bool handleUpdateMessageFlagsEvent(UpdateMessageFlagsEvent event) {
732749
final isAdd = switch (event) {
733750
UpdateMessageFlagsAddEvent() => true,
734751
UpdateMessageFlagsRemoveEvent() => false,
735752
};
736-
737753
if (isAdd && (event as UpdateMessageFlagsAddEvent).all) {
738754
for (final message in messages.values) {
739755
message.flags.add(event.flag);
@@ -766,6 +782,15 @@ class MessageStoreImpl extends HasChannelStore with MessageStore, _OutboxMessage
766782
_notifyMessageListViews(event.messages);
767783
}
768784
}
785+
786+
if (event.flag == MessageFlag.starred) {
787+
isAdd
788+
? starredMessages.addAll(event.messages)
789+
: starredMessages.removeAll(event.messages);
790+
return true;
791+
}
792+
793+
return false;
769794
}
770795

771796
void handleReactionEvent(ReactionEvent event) {

lib/model/store.dart

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,8 @@ class PerAccountStore extends PerAccountStoreBase with
564564
presence: Presence(realm: realm,
565565
initial: initialSnapshot.presences),
566566
channels: channels,
567-
messages: MessageStoreImpl(channels: channels),
567+
messages: MessageStoreImpl(channels: channels,
568+
initialStarredMessages: initialSnapshot.starredMessages),
568569
unreads: Unreads(core: core, channelStore: channels,
569570
initial: initialSnapshot.unreadMsgs),
570571
recentDmConversationsView: RecentDmConversationsView(core: core,
@@ -879,12 +880,16 @@ class PerAccountStore extends PerAccountStoreBase with
879880
// specifically, their `senderId`s. By calling this after the
880881
// aforementioned line, we'll lose reference to those messages.
881882
recentSenders.handleDeleteMessageEvent(event, messages);
882-
_messages.handleDeleteMessageEvent(event);
883+
if (_messages.handleDeleteMessageEvent(event)) {
884+
notifyListeners();
885+
}
883886
unreads.handleDeleteMessageEvent(event);
884887

885888
case UpdateMessageFlagsEvent():
886889
assert(debugLog("server event: update_message_flags/${event.op} ${event.flag.toJson()}"));
887-
_messages.handleUpdateMessageFlagsEvent(event);
890+
if (_messages.handleUpdateMessageFlagsEvent(event)) {
891+
notifyListeners();
892+
}
888893
unreads.handleUpdateMessageFlagsEvent(event);
889894

890895
case SubmessageEvent():

test/model/message_test.dart

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,15 @@ void main() {
5555
Future<void> prepare({
5656
ZulipStream? stream,
5757
bool isChannelSubscribed = true,
58+
List<int>? starredMessages = const [],
5859
int? zulipFeatureLevel,
5960
}) async {
6061
stream ??= eg.stream(streamId: eg.defaultStreamMessageStreamId);
6162
final selfAccount = eg.selfAccount.copyWith(zulipFeatureLevel: zulipFeatureLevel);
6263
store = eg.store(account: selfAccount,
63-
initialSnapshot: eg.initialSnapshot(zulipFeatureLevel: zulipFeatureLevel));
64+
initialSnapshot: eg.initialSnapshot(
65+
starredMessages: starredMessages,
66+
zulipFeatureLevel: zulipFeatureLevel));
6467
await store.addStream(stream);
6568
if (isChannelSubscribed) {
6669
subscription = eg.subscription(stream);
@@ -1634,6 +1637,17 @@ void main() {
16341637
checkNotifiedOnce();
16351638
check(store).messages.values.single.id.equals(message1.id);
16361639
});
1640+
1641+
test('delete a starred message', () async {
1642+
final message = eg.streamMessage(flags: [MessageFlag.starred]);
1643+
await prepare(starredMessages: [message.id]);
1644+
await prepareMessages([message]);
1645+
check(store).starredMessages.single.equals(message.id);
1646+
await store.handleEvent(eg.deleteMessageEvent([message]));
1647+
checkNotifiedOnce();
1648+
check(store).messages.isEmpty();
1649+
check(store).starredMessages.isEmpty();
1650+
});
16371651
});
16381652

16391653
group('handleUpdateMessageFlagsEvent', () {
@@ -1702,6 +1716,30 @@ void main() {
17021716
check(store).messages.values
17031717
.single.flags.deepEquals([MessageFlag.starred, MessageFlag.read]);
17041718
});
1719+
1720+
test('add to starredMessages', () async {
1721+
int perAccountStoreNotifiedCount = 0;
1722+
void checkPerAccountStoreNotified({required int count}) {
1723+
check(perAccountStoreNotifiedCount).equals(count);
1724+
notifiedCount = 0;
1725+
}
1726+
1727+
final message1 = eg.streamMessage(flags: []);
1728+
final message2 = eg.streamMessage(flags: []);
1729+
1730+
await prepare(starredMessages: []);
1731+
1732+
store.addListener(() {
1733+
perAccountStoreNotifiedCount++;
1734+
});
1735+
1736+
await prepareMessages([message1, message2]);
1737+
check(store).starredMessages.isEmpty();
1738+
await store.handleEvent(
1739+
mkAddEvent(MessageFlag.starred, [message1.id, message2.id]));
1740+
checkPerAccountStoreNotified(count: 1);
1741+
check(store).starredMessages.deepEquals([message1.id, message2.id]);
1742+
});
17051743
});
17061744

17071745
group('remove flag', () {
@@ -1737,6 +1775,30 @@ void main() {
17371775
check(store).messages.values
17381776
.single.flags.deepEquals([MessageFlag.starred]);
17391777
});
1778+
1779+
test('remove from starredMessages', () async {
1780+
int perAccountStoreNotifiedCount = 0;
1781+
void checkPerAccountStoreNotified({required int count}) {
1782+
check(perAccountStoreNotifiedCount).equals(count);
1783+
notifiedCount = 0;
1784+
}
1785+
1786+
final message1 = eg.streamMessage(flags: [MessageFlag.starred]);
1787+
final message2 = eg.streamMessage(flags: [MessageFlag.starred]);
1788+
1789+
await prepare(starredMessages: [message1.id, message2.id]);
1790+
1791+
store.addListener(() {
1792+
perAccountStoreNotifiedCount++;
1793+
});
1794+
1795+
await prepareMessages([message1, message2]);
1796+
check(store).starredMessages.deepEquals([message1.id, message2.id]);
1797+
await store.handleEvent(
1798+
mkRemoveEvent(MessageFlag.starred, [message1, message2]));
1799+
checkPerAccountStoreNotified(count: 1);
1800+
check(store).starredMessages.isEmpty();
1801+
});
17401802
});
17411803
});
17421804

test/model/store_checks.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ extension PerAccountStoreChecks on Subject<PerAccountStore> {
6868
Subject<Map<String, ZulipStream>> get streamsByName => has((x) => x.streamsByName, 'streamsByName');
6969
Subject<Map<int, Subscription>> get subscriptions => has((x) => x.subscriptions, 'subscriptions');
7070
Subject<Map<int, Message>> get messages => has((x) => x.messages, 'messages');
71+
Subject<Set<int>> get starredMessages => has((x) => x.starredMessages, 'starredMessages');
7172
Subject<Unreads> get unreads => has((x) => x.unreads, 'unreads');
7273
Subject<RecentDmConversationsView> get recentDmConversationsView => has((x) => x.recentDmConversationsView, 'recentDmConversationsView');
7374
Subject<AutocompleteViewManager> get autocompleteViewManager => has((x) => x.autocompleteViewManager, 'autocompleteViewManager');

0 commit comments

Comments
 (0)