Skip to content

Commit 3409127

Browse files
PIG208gnprice
authored andcommitted
message: Update editState on events.
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 072ad37 commit 3409127

File tree

3 files changed

+132
-4
lines changed

3 files changed

+132
-4
lines changed

lib/model/message.dart

+23-2
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+
if (event.origContent != null) {
129+
// The message is guaranteed to be edited.
130+
// See also: https://zulip.com/api/get-events#update_message
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.");
@@ -168,10 +173,26 @@ class MessageStoreImpl with MessageStore {
168173
return;
169174
}
170175

171-
// final newStreamId = event.newStreamId; // null if topic-only move
172-
// final newTopic = event.newTopic!;
176+
final newTopic = event.newTopic!;
177+
final newChannelId = event.newStreamId; // null if topic-only move
178+
179+
if (newChannelId == null
180+
&& MessageEditState.topicMoveWasResolveOrUnresolve(event.origTopic!, newTopic)) {
181+
// The topic was only resolved/unresolved.
182+
// No change to the messages' editState.
183+
return;
184+
}
185+
173186
// TODO(#150): Handle message moves. The views' recipient headers
174187
// may need updating, and consequently showSender too.
188+
// Currently only editState gets updated.
189+
for (final messageId in event.messageIds) {
190+
final message = messages[messageId];
191+
if (message == null) continue;
192+
// Do not override the edited marker if the message has also been moved.
193+
if (message.editState == MessageEditState.edited) continue;
194+
message.editState = MessageEditState.moved;
195+
}
175196
}
176197

177198
void handleDeleteMessageEvent(DeleteMessageEvent event) {

test/example_data.dart

+32
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,38 @@ UpdateMessageEvent updateMessageEditEvent(
437437
);
438438
}
439439

440+
UpdateMessageEvent updateMessageMoveEvent(
441+
List<Message> messages, {
442+
int? newStreamId,
443+
String? origTopic,
444+
String? newTopic,
445+
String? origContent,
446+
String? newContent,
447+
}) {
448+
assert(messages.isNotEmpty);
449+
final origMessage = messages[0];
450+
final messageId = origMessage.id;
451+
return UpdateMessageEvent(
452+
id: 0,
453+
userId: selfUser.userId,
454+
renderingOnly: false,
455+
messageId: messageId,
456+
messageIds: messages.map((message) => message.id).toList(),
457+
flags: origMessage.flags,
458+
editTimestamp: 1234567890, // TODO generate timestamp
459+
origStreamId: origMessage is StreamMessage ? origMessage.streamId : null,
460+
newStreamId: newStreamId,
461+
propagateMode: null,
462+
origTopic: origTopic,
463+
newTopic: newTopic,
464+
origContent: origContent,
465+
origRenderedContent: origContent,
466+
content: newContent,
467+
renderedContent: newContent,
468+
isMeMessage: false,
469+
);
470+
}
471+
440472
UpdateMessageFlagsRemoveEvent updateMessageFlagsRemoveEvent(
441473
MessageFlag flag,
442474
Iterable<Message> messages, {

test/model/message_test.dart

+77-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,79 @@ 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+
check(store).messages.values.every(((message) => message.editState.equals(MessageEditState.moved)));
298+
});
299+
300+
test('message topic resolved update', () async {
301+
await sendEvent(message, eg.updateMessageMoveEvent([message, otherMessage],
302+
origTopic: 'new topic',
303+
newTopic: '✔ new topic'));
304+
check(store).messages.values.every(((message) => message.editState.equals(MessageEditState.none)));
305+
});
306+
307+
test('message topic unresolved update', () async {
308+
await sendEvent(message, eg.updateMessageMoveEvent([message, otherMessage],
309+
origTopic: '✔ new topic',
310+
newTopic: 'new topic'));
311+
check(store).messages.values.every(((message) => message.editState.equals(MessageEditState.none)));
312+
});
313+
314+
test('message topic both resolved and edited update', () async {
315+
await sendEvent(message, eg.updateMessageMoveEvent([message, otherMessage],
316+
origTopic: 'new topic',
317+
newTopic: '✔ new topic 2'));
318+
check(store).messages.values.every(((message) => message.editState.equals(MessageEditState.moved)));
319+
});
320+
321+
test('message topic both unresolved and edited update', () async {
322+
await sendEvent(message, eg.updateMessageMoveEvent([message, otherMessage],
323+
origTopic: '✔ new topic',
324+
newTopic: 'new topic 2'));
325+
check(store).messages.values.every(((message) => message.editState.equals(MessageEditState.moved)));
326+
});
327+
328+
test('message stream moved update', () async {
329+
await sendEvent(message, eg.updateMessageMoveEvent([message, otherMessage],
330+
origTopic: 'topic',
331+
newTopic: 'topic',
332+
newStreamId: 20));
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+
newStreamId: 20,
341+
origContent: 'old content',
342+
newContent: 'new content'));
343+
check(store).messages[message.id].editState.equals(MessageEditState.edited);
344+
check(store).messages[otherMessage.id].editState.equals(MessageEditState.moved);
345+
});
346+
});
272347
});
273348

274349
group('handleUpdateMessageFlagsEvent', () {

0 commit comments

Comments
 (0)