Skip to content

Commit 6b326ba

Browse files
committed
message: Handle UpdateMessageEvent for moving/editing.
When a channel (a.k.a stream) or a topic gets updated on a message, we get a UpdateMessageEvent from the server. The same happens when the message's content is updated. This updates the edit state to show the "edited" or "moved" marker properly on the messages affected, and leave other move related states (like streamId) untouched until we get to #150. Signed-off-by: Zixuan James Li <[email protected]>
1 parent 62040bc commit 6b326ba

File tree

3 files changed

+136
-3
lines changed

3 files changed

+136
-3
lines changed

lib/model/message.dart

+26-1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ class MessageStoreImpl with MessageStore {
125125
if (message == null) return;
126126

127127
message.flags = event.flags;
128+
// According to the API, the content of the message is edited if origContent
129+
// is not null.
130+
if (event.origContent != null) {
131+
message.editState = MessageEditState.edited;
132+
}
128133
if (event.renderedContent != null) {
129134
assert(message.contentType == 'text/html',
130135
"Message contentType was ${message.contentType}; expected text/html.");
@@ -143,7 +148,10 @@ class MessageStoreImpl with MessageStore {
143148
// The interaction between the fields of these events are a bit tricky.
144149
// For reference, see: https://zulip.com/api/get-events#update_message
145150

146-
if (event.origTopic == null) {
151+
final bool isChannelMoved = event.newStreamId != null;
152+
bool isTopicMoved = event.origTopic != null;
153+
154+
if (!isTopicMoved && !isChannelMoved) {
147155
// There was no move.
148156
assert(() {
149157
if (event.newStreamId != null && event.origStreamId != null
@@ -168,10 +176,27 @@ class MessageStoreImpl with MessageStore {
168176
return;
169177
}
170178

179+
if (isTopicMoved) {
180+
// Eliminate the case when the topic is updated but resolved/unresolved.
181+
isTopicMoved = MessageEditState.areSameTopic(event.origTopic!, event.newTopic!);
182+
}
183+
184+
// When nothing has been moved,
185+
// return early as the messages' editStates need no update.
186+
if (!isTopicMoved && !isChannelMoved) return;
187+
171188
// final newStreamId = event.newStreamId; // null if topic-only move
172189
// final newTopic = event.newTopic!;
173190
// TODO(#150): Handle message moves. The views' recipient headers
174191
// may need updating, and consequently showSender too.
192+
// Currently only editState gets updated.
193+
for (final messageId in event.messageIds) {
194+
final message = messages[messageId];
195+
if (message == null) continue;
196+
// Do not override the edited marker if the message has also been moved.
197+
if (message.editState == MessageEditState.edited) continue;
198+
message.editState = MessageEditState.moved;
199+
}
175200
}
176201

177202
void handleDeleteMessageEvent(DeleteMessageEvent event) {

test/example_data.dart

+32
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,38 @@ UpdateMessageEvent updateMessageEditEvent(
420420
);
421421
}
422422

423+
UpdateMessageEvent updateMessageMoveEvent(
424+
List<Message> messages, {
425+
int? newStreamId,
426+
String? origTopic,
427+
String? newTopic,
428+
String? origContent,
429+
String? origRenderedContent,
430+
}) {
431+
assert(messages.isNotEmpty);
432+
final origMessage = messages[0];
433+
final messageId = origMessage.id;
434+
return UpdateMessageEvent(
435+
id: 0,
436+
userId: selfUser.userId,
437+
renderingOnly: false,
438+
messageId: messageId,
439+
messageIds: messages.map((message) => message.id).toList(),
440+
flags: origMessage.flags,
441+
editTimestamp: 1234567890, // TODO generate timestamp
442+
origStreamId: origMessage is StreamMessage ? origMessage.streamId : null,
443+
newStreamId: newStreamId,
444+
propagateMode: null,
445+
origTopic: origTopic,
446+
newTopic: newTopic,
447+
origContent: origContent,
448+
origRenderedContent: origRenderedContent,
449+
content: 'some probably-mismatched new Markdown',
450+
renderedContent: origMessage.content,
451+
isMeMessage: false,
452+
);
453+
}
454+
423455
UpdateMessageFlagsRemoveEvent updateMessageFlagsRemoveEvent(
424456
MessageFlag flag,
425457
Iterable<Message> messages, {

test/model/message_test.dart

+78-2
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,8 @@ void main() {
207207
..content.not((it) => it.equals(updateEvent.renderedContent!))
208208
..lastEditTimestamp.isNull()
209209
..flags.not((it) => it.deepEquals(updateEvent.flags))
210-
..isMeMessage.not((it) => it.equals(updateEvent.isMeMessage!));
210+
..isMeMessage.not((it) => it.equals(updateEvent.isMeMessage!))
211+
..editState.equals(MessageEditState.none);
211212

212213
await store.handleEvent(updateEvent);
213214
checkNotifiedOnce();
@@ -216,7 +217,8 @@ void main() {
216217
..content.equals(updateEvent.renderedContent!)
217218
..lastEditTimestamp.equals(updateEvent.editTimestamp)
218219
..flags.equals(updateEvent.flags)
219-
..isMeMessage.equals(updateEvent.isMeMessage!);
220+
..isMeMessage.equals(updateEvent.isMeMessage!)
221+
..editState.equals(MessageEditState.edited);
220222
});
221223

222224
test('ignore when message unknown', () async {
@@ -269,6 +271,80 @@ void main() {
269271
test('rendering-only update does not change timestamp (for old server versions)', () async {
270272
await checkRenderingOnly(legacy: true);
271273
});
274+
275+
group('Handle message edit state update', () {
276+
final message = eg.streamMessage();
277+
final otherMessage = eg.streamMessage();
278+
279+
Future<void> sendEvent(Message message, UpdateMessageEvent event) async {
280+
await prepare();
281+
await prepareMessages([message, otherMessage]);
282+
283+
await store.handleEvent(event);
284+
checkNotifiedOnce();
285+
}
286+
287+
test('message not moved update', () async {
288+
await sendEvent(message, eg.updateMessageEditEvent(message));
289+
check(store).messages[message.id].editState.equals(MessageEditState.edited);
290+
check(store).messages[otherMessage.id].editState.equals(MessageEditState.none);
291+
});
292+
293+
test('message topic moved update', () async {
294+
await sendEvent(message, eg.updateMessageMoveEvent([message, otherMessage],
295+
origTopic: 'old topic',
296+
newTopic: 'new topic',
297+
));
298+
check(store).messages.values.every(((message) => message.editState.equals(MessageEditState.moved)));
299+
});
300+
301+
test('message topic resolved update', () async {
302+
await sendEvent(message, eg.updateMessageMoveEvent([message, otherMessage],
303+
origTopic: 'new topic',
304+
newTopic: '✔ new topic',
305+
));
306+
check(store).messages.values.every(((message) => message.editState.equals(MessageEditState.none)));
307+
await sendEvent(message, eg.updateMessageMoveEvent([message, otherMessage],
308+
origTopic: '✔ new topic',
309+
newTopic: 'new topic 2',
310+
));
311+
check(store).messages.values.every(((message) => message.editState.equals(MessageEditState.moved)));
312+
});
313+
314+
test('message topic unresolved update', () async {
315+
await sendEvent(message, eg.updateMessageMoveEvent([message, otherMessage],
316+
origTopic: '✔ new topic',
317+
newTopic: 'new topic',
318+
));
319+
check(store).messages.values.every(((message) => message.editState.equals(MessageEditState.none)));
320+
await sendEvent(message, eg.updateMessageMoveEvent([message, otherMessage],
321+
origTopic: 'new topic',
322+
newTopic: '✔ new topic 2',
323+
));
324+
check(store).messages.values.every(((message) => message.editState.equals(MessageEditState.moved)));
325+
});
326+
327+
test('message stream moved update', () async {
328+
await sendEvent(message, eg.updateMessageMoveEvent([message, otherMessage],
329+
origTopic: 'topic',
330+
newTopic: 'topic',
331+
newStreamId: 20,
332+
));
333+
check(store).messages.values.every(((message) => message.editState.equals(MessageEditState.moved)));
334+
});
335+
336+
test('message is both moved and updated', () async {
337+
await sendEvent(message, eg.updateMessageMoveEvent([message, otherMessage],
338+
origTopic: 'topic',
339+
newTopic: 'topic',
340+
origContent: 'old content',
341+
origRenderedContent: 'old content',
342+
newStreamId: 20,
343+
));
344+
check(store).messages[message.id].editState.equals(MessageEditState.edited);
345+
check(store).messages[otherMessage.id].editState.equals(MessageEditState.moved);
346+
});
347+
});
272348
});
273349

274350
group('handleUpdateMessageFlagsEvent', () {

0 commit comments

Comments
 (0)