Skip to content

Commit fc7258b

Browse files
gnpricechrisbobbe
andcommitted
message: Mutate messages for message edits here, instead of in msglists
Like a recent commit did for reactions, this fixes the part of 455 that's concerned with message edits. With message edits, unlike reactions, the double-processing issue when there are two or more message lists wasn't user-facing, since the mutation done by message lists was (naturally) idempotent. So there isn't really an easy or helpful regression test to write for that. But we can and do write one against the dropped-update symptom when there are zero message lists open. For an explanation of that symptom, see the recent commit where we dealt with reactions. Co-authored-by: Chris Bobbe <[email protected]> Fixes-partly: #455
1 parent 5463328 commit fc7258b

File tree

3 files changed

+80
-41
lines changed

3 files changed

+80
-41
lines changed

lib/model/message.dart

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,38 @@ class MessageStoreImpl with MessageStore {
9393
}
9494

9595
void handleUpdateMessageEvent(UpdateMessageEvent event) {
96+
_handleUpdateMessageEventContent(event);
97+
// TODO(#150): Handle message moves. The views' recipient headers
98+
// may need updating, and consequently showSender too.
99+
}
100+
101+
void _handleUpdateMessageEventContent(UpdateMessageEvent event) {
102+
final message = messages[event.messageId];
103+
if (message == null) return;
104+
105+
// TODO(server-5): Cut this fallback; rely on renderingOnly from FL 114
106+
final isRenderingOnly = event.renderingOnly ?? (event.userId == null);
107+
if (event.editTimestamp != null && !isRenderingOnly) {
108+
// A rendering-only update gets omitted from the message edit history,
109+
// and [Message.lastEditTimestamp] is the last timestamp of that history.
110+
// So on a rendering-only update, the timestamp doesn't get updated.
111+
message.lastEditTimestamp = event.editTimestamp;
112+
}
113+
114+
message.flags = event.flags;
115+
116+
if (event.renderedContent != null) {
117+
assert(message.contentType == 'text/html',
118+
"Message contentType was ${message.contentType}; expected text/html.");
119+
message.content = event.renderedContent!;
120+
}
121+
122+
if (event.isMeMessage != null) {
123+
message.isMeMessage = event.isMeMessage!;
124+
}
125+
96126
for (final view in _messageListViews) {
97-
view.handleUpdateMessageEvent(event); // TODO update mainly in [messages] instead
127+
view.messageContentChanged(event.messageId);
98128
}
99129
}
100130

lib/model/message_list.dart

Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -452,46 +452,12 @@ class MessageListView with ChangeNotifier, _MessageSequence {
452452
}
453453
}
454454

455-
static void _applyChangesToMessage(UpdateMessageEvent event, Message message) {
456-
// TODO(server-5): Cut this fallback; rely on renderingOnly from FL 114
457-
final isRenderingOnly = event.renderingOnly ?? (event.userId == null);
458-
if (event.editTimestamp != null && !isRenderingOnly) {
459-
// A rendering-only update gets omitted from the message edit history,
460-
// and [Message.lastEditTimestamp] is the last timestamp of that history.
461-
// So on a rendering-only update, the timestamp doesn't get updated.
462-
message.lastEditTimestamp = event.editTimestamp;
463-
}
464-
465-
message.flags = event.flags;
466-
467-
if (event.renderedContent != null) {
468-
assert(message.contentType == 'text/html',
469-
"Message contentType was ${message.contentType}; expected text/html.");
470-
message.content = event.renderedContent!;
471-
}
472-
473-
if (event.isMeMessage != null) {
474-
message.isMeMessage = event.isMeMessage!;
475-
}
476-
}
477-
478-
/// Update the message the given event applies to, if present in this view.
479-
///
480-
/// This method only handles the case where the message's contents
481-
/// were changed, and ignores any changes to its stream or topic.
482-
///
483-
/// TODO(#150): Handle message moves.
484-
// NB that when handling message moves (#150), recipient headers
485-
// may need updating, and consequently showSender too.
486-
void handleUpdateMessageEvent(UpdateMessageEvent event) {
487-
final idx = _findMessageWithId(event.messageId);
488-
if (idx == -1) {
489-
return;
455+
void messageContentChanged(int messageId) {
456+
final index = _findMessageWithId(messageId);
457+
if (index != -1) {
458+
_reparseContent(index);
459+
notifyListeners();
490460
}
491-
492-
_applyChangesToMessage(event, messages[idx]);
493-
_reparseContent(idx);
494-
notifyListeners();
495461
}
496462

497463
void maybeUpdateMessageFlags(UpdateMessageFlagsEvent event) {

test/model/message_list_test.dart

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,39 @@ void main() {
297297
});
298298
});
299299

300+
group('messageContentChanged', () {
301+
test('message present', () async {
302+
await prepare(narrow: const CombinedFeedNarrow());
303+
await prepareMessages(foundOldest: false,
304+
messages: List.generate(10, (i) => eg.streamMessage(id: 10 + i)));
305+
306+
final message = model.messages[5];
307+
await store.handleEvent(eg.updateMessageEditEvent(message,
308+
renderedContent: '${message.content}<p>edited</p'));
309+
checkNotifiedOnce();
310+
});
311+
312+
test('message absent', () async {
313+
final stream = eg.stream();
314+
final narrow = StreamNarrow(stream.streamId);
315+
await prepare(narrow: narrow);
316+
317+
final messagesInNarrow = List<Message>.generate(10,
318+
(i) => eg.streamMessage(id: 10 + i, stream: stream));
319+
check(messagesInNarrow.every(narrow.containsMessage)).isTrue();
320+
321+
final messageNotInNarrow = eg.dmMessage(id: 100, from: eg.otherUser, to: [eg.selfUser]);
322+
check(narrow.containsMessage(messageNotInNarrow)).isFalse();
323+
324+
await prepareMessages(foundOldest: false, messages: messagesInNarrow);
325+
await store.addMessage(messageNotInNarrow);
326+
327+
await store.handleEvent(eg.updateMessageEditEvent(messageNotInNarrow,
328+
renderedContent: '${messageNotInNarrow.content}<p>edited</p'));
329+
checkNotNotified();
330+
});
331+
});
332+
300333
group('maybeUpdateMessageFlags', () {
301334
UpdateMessageFlagsAddEvent mkAddEvent(
302335
MessageFlag flag,
@@ -476,7 +509,17 @@ void main() {
476509
doCheckMessageAfterFetch:
477510
(messageSubject) => messageSubject.reactions.isNotNull().total.equals(1),
478511
),
479-
// TODO(#455) message edits; message flags
512+
(
513+
eventName: 'UpdateMessageEvent (edit)',
514+
mkEvent: (message) {
515+
final newContent = '${message.content}<p>edited</p>';
516+
return eg.updateMessageEditEvent(message,
517+
renderedContent: newContent);
518+
},
519+
doCheckMessageAfterFetch:
520+
(messageSubject) => messageSubject.content.endsWith('<p>edited</p>'),
521+
),
522+
// TODO(#455) message flags
480523
]) {
481524
test('$eventName is applied even when message not in any msglists', () async {
482525
final stream = eg.stream();

0 commit comments

Comments
 (0)