Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle moved messages in MessageStore and message lists #787

Merged
merged 9 commits into from
Aug 9, 2024
2 changes: 1 addition & 1 deletion integration_test/unreadmarker_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ void main() {
newestResult(foundOldest: true, messages: messages).toJson());

await tester.pumpWidget(TestZulipApp(accountId: eg.selfAccount.id,
child: const MessageListPage(narrow: CombinedFeedNarrow())));
child: const MessageListPage(initNarrow: CombinedFeedNarrow())));
await tester.pumpAndSettle();
return messages;
}
Expand Down
10 changes: 7 additions & 3 deletions lib/api/model/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ sealed class Message {
final int senderId;
final String senderRealmStr;
@JsonKey(name: 'subject')
final String topic;
String topic;
// final List<string> submessages; // TODO handle
final int timestamp;
String get type;
Expand Down Expand Up @@ -576,8 +576,12 @@ class StreamMessage extends Message {
@JsonKey(includeToJson: true)
String get type => 'stream';

final String displayRecipient;
final int streamId;
// This is not nullable API-wise, but if the message moves across channels,
// [displayRecipient] still refers to the original channel and it has to be
// invalidated.
@JsonKey(required: true, disallowNullValue: true)
String? displayRecipient;
int streamId;

StreamMessage({
required super.client,
Expand Down
113 changes: 64 additions & 49 deletions lib/api/model/model.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 43 additions & 14 deletions lib/model/message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ class MessageStoreImpl with MessageStore {
final newStreamId = event.newStreamId; // null if topic-only move
final origTopic = event.origTopic;
final newTopic = event.newTopic;
final propagateMode = event.propagateMode;

if (origTopic == null) {
// There was no move.
Expand All @@ -167,33 +168,61 @@ class MessageStoreImpl with MessageStore {
return;
}

if (newTopic == null) {
// The `subject` field (aka newTopic) is documented to be present on moves.
assert(debugLog('Malformed UpdateMessageEvent: move but no newTopic')); // TODO(log)
if (newStreamId == null && newTopic == null) {
// If neither the channel nor topic name changed, nothing moved.
// In that case `orig_subject` (aka origTopic) should have been null.
assert(debugLog('Malformed UpdateMessageEvent: move but no newStreamId or newTopic')); // TODO(log)
return;
}
if (origStreamId == null) {
// The `stream_id` field (aka origStreamId) is documented to be present on moves.
assert(debugLog('Malformed UpdateMessageEvent: move but no origStreamId')); // TODO(log)
return;
}

if (newStreamId == null
&& MessageEditState.topicMoveWasResolveOrUnresolve(origTopic, newTopic)) {
// The topic was only resolved/unresolved.
// No change to the messages' editState.
if (propagateMode == null) {
// The `propagate_mode` field (aka propagateMode) is documented to be present on moves.
assert(debugLog('Malformed UpdateMessageEvent: move but no propagateMode')); // TODO(log)
return;
}

// TODO(#150): Handle message moves. The views' recipient headers
// may need updating, and consequently showSender too.
// Currently only editState gets updated.
final wasResolveOrUnresolve = (newStreamId == null
&& MessageEditState.topicMoveWasResolveOrUnresolve(origTopic, newTopic!));

for (final messageId in event.messageIds) {
final message = messages[messageId];
if (message == null) continue;
// Do not override the edited marker if the message has also been moved.
if (message.editState == MessageEditState.edited) continue;
message.editState = MessageEditState.moved;

if (message is! StreamMessage) {
assert(debugLog('Bad UpdateMessageEvent: stream/topic move on a DM')); // TODO(log)
continue;
}

if (newStreamId != null) {
message.streamId = newStreamId;
// See [StreamMessage.displayRecipient] on why the invalidation is
// needed.
message.displayRecipient = null;
}

if (newTopic != null) {
message.topic = newTopic;
}

if (!wasResolveOrUnresolve
&& message.editState == MessageEditState.none) {
message.editState = MessageEditState.moved;
}
}

for (final view in _messageListViews) {
view.messagesMoved(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit in commit message:

Signed-off-by: Zixuan James Li <[email protected]>
Co-authored-by: Zixuan James Li <[email protected]>

If we're going to have "Signed-off-by:" lines, let's follow the conventions of how they're used. It's a trailer that comes after the material you're signing off on. Are you signing off on the parts of this commit which you co-authored, or only on other parts? If the former, your "Signed-off-by:" should come after your "Co-authored-by:". (And if the latter, it's not clear what the line could really mean and it's probably best to just leave it out.)

For example usage, go into the Linux kernel repo and try a command like git log --grep Co-author.

origStreamId: origStreamId,
newStreamId: newStreamId ?? origStreamId,
origTopic: origTopic,
newTopic: newTopic ?? origTopic,
messageIds: event.messageIds,
propagateMode: propagateMode,
);
}
}

Expand Down
Loading